Compare commits

...

58 Commits

Author SHA1 Message Date
semantic-release-bot
9523ae12c0 chore(release): 5.0.0-preview.16 [skip ci]
# [5.0.0-preview.16](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/5.0.0-preview.15...5.0.0-preview.16) (2025-03-15)

### Bug Fixes

* IL2CPP build fails on older versions of Unity ([0da6525](0da652520c))
* NRE on enable ([0cff50e](0cff50ef69)), closes [#359](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/359)
2025-03-15 08:12:51 +00:00
mob-sakai
96106370e5 Merge remote-tracking branch 'origin/develop' into develop-preview
# Conflicts:
#	Packages/src/CHANGELOG.md
#	Packages/src/package.json
2025-03-15 17:02:36 +09:00
semantic-release-bot
bb2d588e0c chore(release): 4.11.2 [skip ci]
## [4.11.2](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.11.1...v4.11.2) (2025-03-15)

### Bug Fixes

* IL2CPP build fails on older versions of Unity ([0da6525](0da652520c))
* NRE on enable ([0cff50e](0cff50ef69)), closes [#359](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/359)
2025-03-15 07:59:37 +00:00
mob-sakai
078005a1a7 chore: fix NanoMonitor link url 2025-03-15 16:58:52 +09:00
mob-sakai
0cff50ef69 fix: NRE on enable
close #359
2025-03-14 19:44:02 +09:00
mob-sakai
0da652520c fix: IL2CPP build fails on older versions of Unity 2025-03-14 19:29:41 +09:00
mob-sakai
4199492e3a chore: update internal 2025-03-14 19:29:22 +09:00
mob-sakai
1f88bb255e chore: fix settings icon 2025-03-14 19:24:22 +09:00
semantic-release-bot
73d208e9b8 chore(release): 5.0.0-preview.15 [skip ci]
# [5.0.0-preview.15](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/5.0.0-preview.14...5.0.0-preview.15) (2025-02-21)

### Bug Fixes

* component icons will no longer be displayed in the scene view ([6dfbdae](6dfbdae38d))
* editor crashed on exit play mode (editor, windows) ([47ee45c](47ee45cbbe)), closes [#351](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/351)

### Features

* add 'TimeScaleMultiplier' option ([925af0b](925af0b604))
2025-02-21 09:45:26 +00:00
mob-sakai
bfac4d4e2f Merge remote-tracking branch 'origin/develop' into develop-preview
# Conflicts:
#	Packages/src/CHANGELOG.md
#	Packages/src/Runtime/UIParticle.cs
#	Packages/src/package.json
2025-02-21 18:38:11 +09:00
semantic-release-bot
e3791866b7 chore(release): 4.11.1 [skip ci]
## [4.11.1](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.11.0...v4.11.1) (2025-02-21)

### Bug Fixes

* component icons will no longer be displayed in the scene view ([6dfbdae](6dfbdae38d))
2025-02-21 09:35:05 +00:00
mob-sakai
6dfbdae38d fix: component icons will no longer be displayed in the scene view 2025-02-21 18:22:24 +09:00
mob-sakai
b63220b871 doc: update readme 2025-02-21 17:57:12 +09:00
semantic-release-bot
3fd2bea599 chore(release): 4.11.0 [skip ci]
# [4.11.0](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.7...v4.11.0) (2025-02-21)

### Features

* add 'TimeScaleMultiplier' option ([925af0b](925af0b604))
2025-02-21 07:17:09 +00:00
mob-sakai
925af0b604 feat: add 'TimeScaleMultiplier' option 2025-02-21 16:10:06 +09:00
semantic-release-bot
d1a1e23e50 chore(release): 4.10.7 [skip ci]
## [4.10.7](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.6...v4.10.7) (2025-01-14)

### Bug Fixes

* editor crashed on exit play mode (editor, windows) ([47ee45c](47ee45cbbe)), closes [#351](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/351)
2025-01-14 11:49:21 +00:00
mob-sakai
47ee45cbbe fix: editor crashed on exit play mode (editor, windows)
close #351
2025-01-07 10:25:23 +09:00
semantic-release-bot
88d956ece8 chore(release): 5.0.0-preview.14 [skip ci]
# [5.0.0-preview.14](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/5.0.0-preview.13...5.0.0-preview.14) (2025-01-03)

### Bug Fixes

* sub-emitter particles may not render correctly in certain scenarios ([8276684](8276684c3b)), closes [#348](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/348)
* sub-emitter's `inherit velocity` module doubles at runtime ([67de3d1](67de3d1bd3)), closes [#349](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/349)
2025-01-03 14:26:44 +00:00
semantic-release-bot
1b0110320b chore(release): 4.10.6 [skip ci]
## [4.10.6](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.5...v4.10.6) (2025-01-03)

### Bug Fixes

* sub-emitter particles may not render correctly in certain scenarios ([8276684](8276684c3b)), closes [#348](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/348)
* sub-emitter's `inherit velocity` module doubles at runtime ([67de3d1](67de3d1bd3)), closes [#349](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/349)
2025-01-03 14:24:40 +00:00
mob-sakai
2842b3db7d Merge branch 'develop' into develop-preview 2025-01-03 23:14:17 +09:00
mob-sakai
a1ca74f854 chore: update coffee.internal 2025-01-03 23:13:17 +09:00
mob-sakai
8276684c3b fix: sub-emitter particles may not render correctly in certain scenarios
close #348
2025-01-03 23:13:17 +09:00
mob-sakai
67de3d1bd3 fix: sub-emitter's inherit velocity module doubles at runtime
close #349
2025-01-03 23:13:17 +09:00
semantic-release-bot
c2958d5d7d chore(release): 5.0.0-preview.13 [skip ci]
# [5.0.0-preview.13](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/5.0.0-preview.12...5.0.0-preview.13) (2025-01-03)

### Features

* change the default value of `UIParticle.scale` from `10` to `1` ([9955eef](9955eefdc2))
* UIParticle no longer inherits from MaskableGraphic ([c09bfb8](c09bfb81ab))

### BREAKING CHANGES

* Some members inherited from MaskableGraphic will no longer be available.
2025-01-03 14:12:36 +00:00
mob-sakai
7456b5d555 docs: update documents 2025-01-03 23:08:20 +09:00
mob-sakai
9955eefdc2 feat: change the default value of UIParticle.scale from 10 to 1 2025-01-03 23:02:15 +09:00
mob-sakai
c09bfb81ab feat: UIParticle no longer inherits from MaskableGraphic
BREAKING CHANGE: Some members inherited from MaskableGraphic will no longer be available.
2025-01-03 23:02:15 +09:00
mob-sakai
fab2ed1697 chore: update coffee.internal 2025-01-03 22:58:23 +09:00
mob-sakai
ef4dbc3cba fix: sub-emitter particles may not render correctly in certain scenarios
close #248
2025-01-03 22:50:36 +09:00
mob-sakai
27551a4d47 fix: sub-emitter's inherit velocity module doubles at runtime
close #249
2025-01-03 22:29:07 +09:00
semantic-release-bot
90d966e659 chore(release): 4.10.5 [skip ci]
## [4.10.5](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.4...v4.10.5) (2024-12-23)

### Bug Fixes

* '3D' scale toggle in the inspector does not keep on reload ([934f4b8](934f4b8f1c)), closes [#346](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/346)
2024-12-23 03:54:11 +00:00
mob-sakai
0036b03d12 refactor: update coffee.internal 2024-12-23 12:53:15 +09:00
mob-sakai
934f4b8f1c fix: '3D' scale toggle in the inspector does not keep on reload
close #346
2024-12-22 13:10:21 +09:00
semantic-release-bot
fe4fcc5ddd chore(release): 4.10.4 [skip ci]
## [4.10.4](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.3...v4.10.4) (2024-12-19)

### Bug Fixes

* rendering issues when playing with opening a prefab stage ([95235a9](95235a929b)), closes [#345](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/345)
2024-12-19 08:50:19 +00:00
mob-sakai
072aac521b chore: update test workflow 2024-12-18 19:50:31 +09:00
mob-sakai
95235a929b fix: rendering issues when playing with opening a prefab stage
close #345
2024-12-18 19:46:32 +09:00
semantic-release-bot
1a748f19d0 chore(release): 4.10.3 [skip ci]
## [4.10.3](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.2...v4.10.3) (2024-11-20)

### Bug Fixes

* if not configured as a preloaded asset, the project settings asset will be regenerated ([abe0948](abe09485f6)), closes [#342](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/342)
2024-11-20 16:56:36 +00:00
mob-sakai
f9ce2c6c96 chore: update workflows 2024-11-21 01:46:37 +09:00
mob-sakai
abe09485f6 fix: if not configured as a preloaded asset, the project settings asset will be regenerated
close #342
2024-11-21 01:39:16 +09:00
mob-sakai
253fb52650 refactor: update coffee.internal 2024-11-21 01:36:51 +09:00
mob-sakai
3544c593e2 chore: add demo for cfxrf 2024-11-21 01:26:03 +09:00
semantic-release-bot
4faf151c87 chore(release): 4.10.2 [skip ci]
## [4.10.2](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.1...v4.10.2) (2024-11-01)

### Bug Fixes

* trail incorrect offset ([afe00a1](afe00a1dde)), closes [#335](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/335)
2024-11-01 02:15:40 +00:00
Jie
afe00a1dde fix: trail incorrect offset
close #335
2024-11-01 11:14:03 +09:00
mob-sakai
c1e3d68f60 refactor 2024-09-30 03:01:48 +09:00
semantic-release-bot
a1ea8785bc chore(release): 4.10.1 [skip ci]
## [4.10.1](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.0...v4.10.1) (2024-09-29)

### Bug Fixes

* mainTex will be ignored ([2ee69d0](2ee69d0424))
2024-09-29 17:46:35 +00:00
mob-sakai
2ee69d0424 fix: mainTex will be ignored 2024-09-30 02:36:45 +09:00
semantic-release-bot
2c1ac4b5e9 chore(release): 4.10.0 [skip ci]
# [4.10.0](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.9.1...v4.10.0) (2024-09-29)

### Bug Fixes

* component icon is not set ([5ff6ec8](5ff6ec815a))

### Features

* add project settings ([1ce4e31](1ce4e31a96))
2024-09-29 17:14:48 +00:00
mob-sakai
5f255ec6cd demo: fix warning 2024-09-30 02:12:44 +09:00
mob-sakai
1b678c68c6 chore: update workflow 2024-09-30 01:56:54 +09:00
mob-sakai
5ff6ec815a fix: component icon is not set 2024-09-30 01:56:54 +09:00
mob-sakai
0ee1671246 docs: update readme 2024-09-30 01:56:54 +09:00
mob-sakai
6a7f08c056 style: format 2024-09-30 01:56:54 +09:00
mob-sakai
1ce4e31a96 feat: add project settings 2024-09-30 01:56:54 +09:00
mob-sakai
c08f0d492f refactor: using Coffee.Internal 2024-09-30 01:48:06 +09:00
mob-sakai
7069b396e4 import Coffee.Internal 2024-09-30 01:48:06 +09:00
semantic-release-bot
109caed657 chore(release): 4.9.1 [skip ci]
## [4.9.1](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.9.0...v4.9.1) (2024-08-07)

### Bug Fixes

* ParticleSystem trails gain offset on parent canvas change ([2a1cd50](2a1cd502b4)), closes [#323](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/323)
2024-08-07 17:13:07 +00:00
mob-sakai
2a1cd502b4 fix: ParticleSystem trails gain offset on parent canvas change
close #323
2024-08-08 02:00:40 +09:00
mob-sakai
78d0219235 update demo 2024-07-18 14:56:49 +09:00
77 changed files with 9955 additions and 2975 deletions

6
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,6 @@
# This is a comment.
# Each line is a file pattern followed by one or more owners.
# https://docs.github.com/ja/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
# Default owners
* @mob-sakai

12
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: mob-sakai # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: mob_sakai # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

35
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,35 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: mob-sakai
---
NOTE: Your issue may already be reported! Please search on the [issue tracker](../) before creating one.
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Environment (please complete the following information):**
- Version [e.g. 1.0.0]
- Platform: [e.g. Editor(Windows/Mac), Standalone(Windows/Mac), iOS, Android, WebGL]
- Unity version: [e.g. 2018.2.8f1]
- Build options: [e.g. IL2CPP, .Net 4.x, LWRP]
**Additional context**
Add any other context about the problem here.

View File

@@ -0,0 +1,22 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: mob-sakai
---
NOTE: Your issue may already be reported! Please search on the [issue tracker](../) before creating one.
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

16
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@@ -0,0 +1,16 @@
---
name: Question
about: Ask a question about this project
title: ''
labels: question
assignees: mob-sakai
---
NOTE: Your issue may already be reported! Please search on the [issue tracker](../) before creating one.
**Describe what help do you need**
A description of the question.
**Additional context**
Add any other context or screenshots about the question here.

37
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,37 @@
# Pull Request Template
## Description
- Please include a summary of the change and which issue is fixed.
- Please also include relevant motivation and context.
- List any dependencies that are required for this change.
Fixes #{issue_number}
## Type of change
Please write the commit message in the format corresponding to the change type.
Please see [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for more information.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Update documentations
- [ ] Others (refactoring, style changes, etc.)
## Test environment
- Platform: [e.g. Editor(Windows/Mac), Standalone(Windows/Mac), iOS, Android, WebGL]
- Unity version: [e.g. 2022.2.0f1]
- Build options: [e.g. IL2CPP, .Net 4.x, URP/HDRP]
## Checklist
- [ ] This pull request is for merging into the `develop` branch
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have checked my code and corrected any misspellings

28
.github/workflows/deploy.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: 🚀 Deploy with Zip
on:
workflow_dispatch:
inputs:
zip:
description: "The url to the zip file"
required: true
jobs:
deploy:
name: 🚀 Deploy
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
steps:
- name: 📦 Download zip file To '_site'
run: |
curl -L ${{ github.event.inputs.zip }} -o _site.zip
unzip _site.zip -d _site
find _site -name __MACOSX | xargs rm -rf
- name: 📦 Upload '_site'
uses: actions/upload-pages-artifact@v3
- name: 🚀 Deploy To GitHub Pages
uses: actions/deploy-pages@v4

View File

@@ -1,12 +1,13 @@
name: 🔖 Release
run-name: 🔖 Release (${{ github.ref_name }})
on:
workflow_dispatch:
push:
branches:
- release
- release-preview
- release-v4
- preview
- main
- v*.x
tags-ignore:
- "**"
@@ -22,9 +23,6 @@ jobs:
channel: ${{ steps.release.outputs.new_release_channel }}
released: ${{ steps.release.outputs.new_release_published }}
tag: ${{ steps.release.outputs.new_release_git_tag }}
version: ${{ steps.release.outputs.new_release_version }}
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,66 +36,9 @@ jobs:
@semantic-release/changelog
@semantic-release/git
env:
GITHUB_TOKEN: ${{ github.token }}
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
- id: summary
run: |
- 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:
if: needs.release.outputs.merge_to != ''
needs: release
name: 🔀 Merge to ${{ needs.release.outputs.merge_to }}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: 🚚 Checkout (${{ needs.release.outputs.merge_to }})
uses: actions/checkout@v4
with:
ref: ${{ needs.release.outputs.merge_to }}
fetch-depth: 0
- 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 ${{ needs.release.outputs.merge_to }}
split-to:
if: needs.release.outputs.split_to != ''
needs: release
name: 🔀 Split package to ${{ needs.release.outputs.split_to }}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: 🚚 Checkout (${{ needs.release.outputs.tag }})
uses: actions/checkout@v4
with:
ref: ${{ needs.release.outputs.tag }}
fetch-depth: 0
- name: 🔀 Split subtree 'Packages/src' to '${{ needs.release.outputs.split_to }}'
run: |
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

@@ -3,28 +3,36 @@
# UNITY_EMAIL: Unity user email to login
# UNITY_PASSWORD: Unity user password to login
name: 🧪 Test
run-name: 🧪 Test (${{ github.event.pull_request.title || github.ref_name }})
env:
# MINIMUM_VERSION: The minimum version of Unity.
MINIMUM_VERSION: 2019.4
# EXCLUDE_FILTER: The excluded versions of Unity.
EXCLUDE_FILTER: '(2020.2.0)'
EXCLUDE_FILTER: "(2020.2.0|2021.1|2023.3)"
on:
workflow_dispatch:
inputs:
usePeriodVersions:
description: "Use the period versions (.0f1, .10f1, 20f1, ...)."
required: false
default: "true"
push:
branches:
- develop
- develop-preview
- develop-4.x
tags:
- "!*"
paths-ignore:
- "*.md"
pull_request:
- "**.md"
pull_request_target:
types:
- opened
- reopened
- synchronize
paths-ignore:
- "**.md"
jobs:
setup:
@@ -37,9 +45,12 @@ jobs:
id: setup
run: |
echo "==== Target Unity Versions ===="
LATEST_VERSIONS=`npx unity-changeset list --versions --latest-patch --min ${MINIMUM_VERSION} --json --all`
# ADDITIONAL_VERSIONS=`npx unity-changeset list --versions --grep '0f' --min ${MINIMUM_VERSION} --json`
ADDITIONAL_VERSIONS=[]
LATEST_VERSIONS=`npx unity-changeset@latest list --versions --latest-patch --min ${MINIMUM_VERSION} --json --all`
if [ "${{ inputs.usePeriodVersions }}" = "true" ]; then
ADDITIONAL_VERSIONS=`npx unity-changeset list --versions --grep '0f' --min ${MINIMUM_VERSION} --json`
else
ADDITIONAL_VERSIONS=[]
fi
VERSIONS=`echo "[${LATEST_VERSIONS}, ${ADDITIONAL_VERSIONS}]" \
| jq -c '[ flatten | sort | unique | .[] | select( test("${{ env.EXCLUDE_FILTER }}") | not ) ]'`
@@ -48,33 +59,44 @@ jobs:
test:
name: 🧪 Run tests
runs-on: ubuntu-latest
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
permissions:
checks: write
contents: read
needs: setup
strategy:
fail-fast: false
max-parallel: 4
max-parallel: 6
matrix:
unityVersion: ${{ fromJson(needs.setup.outputs.unityVersions) }}
steps:
- name: 🚚 Checkout
- name: 🚚 Checkout ($${{ github.ref }})
if: github.event_name == 'push'
uses: actions/checkout@v4
- name: 🚚 Checkout pull request (pull_request_target)
if: github.event_name == 'pull_request_target'
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: 🚚 Marge pull request (pull_request_target)
if: github.event_name == 'pull_request_target'
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git merge origin/${{ github.event.pull_request.base.ref }} --no-edit
- name: 📥 Cache library
uses: actions/cache@v4
with:
path: Library
key: Library-${{ matrix.unityVersion }}-${{ github.sha }}
key: Library-${{ matrix.unityVersion }}-${{ github.event.pull_request.head.sha || github.sha }}
restore-keys: |
Library-${{ matrix.unityVersion }}-
Library-
- name: 🛠️ Build Unity Project
- name: 🛠️ Build Unity Project (Test)
uses: game-ci/unity-builder@v4
timeout-minutes: 45
with:
@@ -82,6 +104,10 @@ jobs:
targetPlatform: StandaloneLinux64
allowDirtyBuild: true
customParameters: -nographics
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
- name: 🧪 Run tests
uses: game-ci/unity-test-runner@v4
@@ -92,4 +118,7 @@ jobs:
customParameters: -nographics
checkName: ${{ matrix.unityVersion }} Test Results
githubToken: ${{ github.token }}
coverageOptions: "dontClear;generateHtmlReport;generateBadgeReport;pathFilters:+**/Packages/src/**;assemblyFilters:+<packages>,-*.Editor,-*.Test"
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: e34a092899a9547189add96707de1b5a
DefaultImporter:
userData:

View File

@@ -80,7 +80,11 @@ namespace Coffee.UIExtensions.Demo
.SelectMany(x => x.GetTypes())
.FirstOrDefault(x => x.Name == typeName);
#if UNITY_2023_2_OR_NEWER
return type == null ? null : FindFirstObjectByType(type);
#else
return type == null ? null : FindObjectOfType(type);
#endif
}
public void SetCanvasWidth(int width)

View File

@@ -1,3 +1,4 @@
using Coffee.UIParticleInternal;
using UnityEngine;
using UnityEngine.Serialization;
@@ -46,11 +47,7 @@ namespace Coffee.UIExtensions.Demo
if (!flag)
{
#if UNITY_2023_1_OR_NEWER
foreach (var ps in FindObjectsByType<ParticleSystem>(FindObjectsInactive.Include, FindObjectsSortMode.None))
#else
foreach (var ps in FindObjectsOfType<ParticleSystem>())
#endif
foreach (var ps in Misc.FindObjectsOfType<ParticleSystem>())
{
ps.Play(false);
}
@@ -79,11 +76,7 @@ namespace Coffee.UIExtensions.Demo
public void ParticleSystem_SetScale(float scale)
{
#if UNITY_2023_1_OR_NEWER
foreach (var ps in FindObjectsByType<ParticleSystem>(FindObjectsInactive.Include, FindObjectsSortMode.None))
#else
foreach (var ps in FindObjectsOfType<ParticleSystem>())
#endif
foreach (var ps in Misc.FindObjectsOfType<ParticleSystem>())
{
ps.transform.localScale = new Vector3(scale, scale, scale);
}

View File

@@ -18,11 +18,12 @@ MonoBehaviour:
m_EnabledInEditor: 1
m_AlwaysIncludeAssembly: 1
m_InstantiateOnLoad: 1
m_Prefab: {fileID: 7211429669315726685, guid: b73940fc30a2f4eb9a73783e9c1f8da6,
m_Prefab: {fileID: 4567906826058368312, guid: 7cebff2d255b9433cbe23b243c193329,
type: 3}
m_Interval: 0.5
m_Anchor: 0
m_Width: 750
m_HelpUrl: https://github.com/mob-sakai/ParticleEffectForUGUI
m_CustomMonitorItems:
- m_Format: Screen:{0}x{1}
m_Arg0:
@@ -31,3 +32,10 @@ MonoBehaviour:
m_Path: UnityEngine.Screen, UnityEngine.CoreModule;height
m_Arg2:
m_Path:
- m_Format: UIParticles:{0} Materials:{1}
m_Arg0:
m_Path: Coffee.UIExtensions.UIParticleUpdater, Coffee.UIParticle;uiParticleCount
m_Arg1:
m_Path: Coffee.UIParticleInternal.MaterialRepository, Coffee.UIParticle;count
m_Arg2:
m_Path:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
%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: UIParticleProjectSettings
m_EditorClassIdentifier:
m_EnableLinearToGamma: 1
m_HideGeneratedObjects: 1

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a5b9278dfbd194d04b1c6ae7031928c1
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,5 +1,7 @@
{
"dependencies": {
"com.coffee.development": "https://github.com/mob-sakai/Coffee.Internal.git?path=Packages/Development",
"com.coffee.minimal-resource": "https://github.com/mob-sakai/Coffee.Internal.git?path=Packages/MinimalResource",
"com.coffee.nano-monitor": "https://github.com/mob-sakai/Coffee.Internal.git?path=Packages/NanoMonitor",
"com.unity.ide.rider": "3.0.31",
"com.unity.test-framework": "1.1.33",

View File

@@ -1,5 +1,19 @@
{
"dependencies": {
"com.coffee.development": {
"version": "https://github.com/mob-sakai/Coffee.Internal.git?path=Packages/Development",
"depth": 0,
"source": "git",
"dependencies": {},
"hash": "3c280f1a8f4db5038b881ff07f270efd9638fa31"
},
"com.coffee.minimal-resource": {
"version": "https://github.com/mob-sakai/Coffee.Internal.git?path=Packages/MinimalResource",
"depth": 0,
"source": "git",
"dependencies": {},
"hash": "3c280f1a8f4db5038b881ff07f270efd9638fa31"
},
"com.coffee.nano-monitor": {
"version": "https://github.com/mob-sakai/Coffee.Internal.git?path=Packages/NanoMonitor",
"depth": 0,
@@ -7,7 +21,7 @@
"dependencies": {
"com.unity.ugui": "1.0.0"
},
"hash": "c51e4514c9ab944915a639433ee52342e55a644e"
"hash": "3c280f1a8f4db5038b881ff07f270efd9638fa31"
},
"com.coffee.ui-particle": {
"version": "file:src",

View File

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

View File

@@ -1,12 +1,13 @@
{
"branches": [
"release",
"release-4.x",
"main",
"[0-9]+.x",
{
"name": "release-preview",
"prerelease": "preview"
"name": "preview",
"prerelease": true
}
],
"tagFormat": "${version}",
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",

View File

@@ -1,3 +1,99 @@
# [5.0.0-preview.16](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/5.0.0-preview.15...5.0.0-preview.16) (2025-03-15)
### Bug Fixes
* IL2CPP build fails on older versions of Unity ([0da6525](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/0da652520cd165b43de7404c0b0ab1fbcf9349d1))
* NRE on enable ([0cff50e](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/0cff50ef696aa53fb7c46a9a737b7cf3a05b7b9b)), closes [#359](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/359)
# [5.0.0-preview.15](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/5.0.0-preview.14...5.0.0-preview.15) (2025-02-21)
### Bug Fixes
* component icons will no longer be displayed in the scene view ([6dfbdae](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/6dfbdae38d3822ab9c2c6f0e4ca1ca32ee98a239))
* editor crashed on exit play mode (editor, windows) ([47ee45c](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/47ee45cbbe651a8f87ca2b8a3948f8b88db8211e)), closes [#351](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/351)
### Features
* add 'TimeScaleMultiplier' option ([925af0b](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/925af0b6046f65f23a778f67cefa8ff9cbedb513))
# [5.0.0-preview.14](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/5.0.0-preview.13...5.0.0-preview.14) (2025-01-03)
### Bug Fixes
* sub-emitter particles may not render correctly in certain scenarios ([8276684](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/8276684c3b1646f0490ed64557547ba15281664a)), closes [#348](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/348)
* sub-emitter's `inherit velocity` module doubles at runtime ([67de3d1](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/67de3d1bd3e16dc9b564625cb990c53d75769506)), closes [#349](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/349)
# [5.0.0-preview.13](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/5.0.0-preview.12...5.0.0-preview.13) (2025-01-03)
### Features
* change the default value of `UIParticle.scale` from `10` to `1` ([9955eef](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/9955eefdc22cf565502f85c87cd2efd3a25fbe50))
* UIParticle no longer inherits from MaskableGraphic ([c09bfb8](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/c09bfb81abc9179bf5fc49d29eaf7fc4ed01a4dc))
### BREAKING CHANGES
* Some members inherited from MaskableGraphic will no longer be available.
## [4.10.5](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.4...v4.10.5) (2024-12-23)
### Bug Fixes
* '3D' scale toggle in the inspector does not keep on reload ([934f4b8](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/934f4b8f1c61f8ff20228d0ebcea9f636a3758ed)), closes [#346](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/346)
## [4.10.4](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.3...v4.10.4) (2024-12-19)
### Bug Fixes
* rendering issues when playing with opening a prefab stage ([95235a9](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/95235a929b82cf681365ed6eba837d857f83e3d2)), closes [#345](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/345)
## [4.10.3](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.2...v4.10.3) (2024-11-20)
### Bug Fixes
* if not configured as a preloaded asset, the project settings asset will be regenerated ([abe0948](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/abe09485f65dd4efd18e74675e752e0213bdf3be)), closes [#342](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/342)
## [4.10.2](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.1...v4.10.2) (2024-11-01)
### Bug Fixes
* trail incorrect offset ([afe00a1](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/afe00a1dde80eb1c0a7bb668b75f4c3733d3fa43)), closes [#335](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/335)
## [4.10.1](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.10.0...v4.10.1) (2024-09-29)
### Bug Fixes
* mainTex will be ignored ([2ee69d0](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/2ee69d04245fabce185f67dc9bd68c870e556564))
# [4.10.0](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.9.1...v4.10.0) (2024-09-29)
### Bug Fixes
* component icon is not set ([5ff6ec8](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/5ff6ec815a174de5d3f16d424f1204c60912a8d8))
### Features
* add project settings ([1ce4e31](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/1ce4e31a9681bf1a201d2723c8d97e07ecc16592))
## [4.9.1](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.9.0...v4.9.1) (2024-08-07)
### Bug Fixes
* ParticleSystem trails gain offset on parent canvas change ([2a1cd50](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/2a1cd502b452b5b56edf8bcfe91adf99d1bb5147)), closes [#323](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/323)
# [4.9.0](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/v4.8.1...v4.9.0) (2024-07-18)

View File

@@ -2,12 +2,11 @@ using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.UI;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.UI;
using Coffee.UIParticleExtensions;
using Coffee.UIParticleInternal;
#if UNITY_2021_2_OR_NEWER
using UnityEditor.Overlays;
#else
@@ -17,6 +16,7 @@ using Object = UnityEngine.Object;
#endif
#if UNITY_2021_2_OR_NEWER
using UnityEditor.SceneManagement;
#elif UNITY_2018_3_OR_NEWER
using UnityEditor.Experimental.SceneManagement;
#endif
@@ -25,8 +25,13 @@ namespace Coffee.UIExtensions
{
[CustomEditor(typeof(UIParticle))]
[CanEditMultipleObjects]
internal class UIParticleEditor : GraphicEditor
internal class UIParticleEditor : Editor
{
internal class State : ScriptableSingleton<State>
{
public bool is3DScaleMode;
}
//################################
// Constant or Static Members.
//################################
@@ -45,7 +50,6 @@ namespace Coffee.UIExtensions
private static readonly GUIContent s_ContentPrimary = new GUIContent("Primary");
private static readonly Regex s_RegexBuiltInGuid = new Regex(@"^0{16}.0{15}$", RegexOptions.Compiled);
private static readonly List<Material> s_TempMaterials = new List<Material>();
private static bool s_XYZMode;
private SerializedProperty _maskable;
private SerializedProperty _scale3D;
@@ -57,8 +61,10 @@ namespace Coffee.UIExtensions
private SerializedProperty _autoScalingMode;
private SerializedProperty _useCustomView;
private SerializedProperty _customViewSize;
private SerializedProperty _timeScaleMultiplier;
private ReorderableList _ro;
private bool _showMax;
private bool _is3DScaleMode;
private static readonly HashSet<Shader> s_Shaders = new HashSet<Shader>();
#if UNITY_2018 || UNITY_2019
@@ -80,10 +86,8 @@ namespace Coffee.UIExtensions
/// <summary>
/// This function is called when the object becomes enabled and active.
/// </summary>
protected override void OnEnable()
private void OnEnable()
{
base.OnEnable();
_maskable = serializedObject.FindProperty("m_Maskable");
_scale3D = serializedObject.FindProperty("m_Scale3D");
_animatableProperties = serializedObject.FindProperty("m_AnimatableProperties");
@@ -94,6 +98,7 @@ namespace Coffee.UIExtensions
_autoScalingMode = serializedObject.FindProperty("m_AutoScalingMode");
_useCustomView = serializedObject.FindProperty("m_UseCustomView");
_customViewSize = serializedObject.FindProperty("m_CustomViewSize");
_timeScaleMultiplier = serializedObject.FindProperty("m_TimeScaleMultiplier");
var sp = serializedObject.FindProperty("m_Particles");
_ro = new ReorderableList(sp.serializedObject, sp, true, true, true, true)
@@ -162,6 +167,19 @@ namespace Coffee.UIExtensions
uip.RefreshParticles(uip.particles);
}
}
// Initialize 3D scale mode.
_is3DScaleMode = State.instance.is3DScaleMode;
if (!_is3DScaleMode)
{
var x = _scale3D.FindPropertyRelative("x");
var y = _scale3D.FindPropertyRelative("y");
var z = _scale3D.FindPropertyRelative("z");
_is3DScaleMode = !Mathf.Approximately(x.floatValue, y.floatValue) ||
!Mathf.Approximately(y.floatValue, z.floatValue) ||
y.hasMultipleDifferentValues ||
z.hasMultipleDifferentValues;
}
}
/// <summary>
@@ -180,7 +198,11 @@ namespace Coffee.UIExtensions
// Scale
EditorGUI.BeginDisabledGroup(!_meshSharing.hasMultipleDifferentValues && _meshSharing.intValue == 4);
s_XYZMode = DrawFloatOrVector3Field(_scale3D, s_XYZMode);
if (DrawFloatOrVector3Field(_scale3D, _is3DScaleMode) != _is3DScaleMode)
{
State.instance.is3DScaleMode = _is3DScaleMode = !_is3DScaleMode;
}
EditorGUI.EndDisabledGroup();
// AnimatableProperties
@@ -221,6 +243,9 @@ namespace Coffee.UIExtensions
_customViewSize.floatValue = Mathf.Max(0.1f, _customViewSize.floatValue);
}
// Time Scale Multiplier
EditorGUILayout.PropertyField(_timeScaleMultiplier);
// Target ParticleSystems.
EditorGUI.BeginChangeCheck();
_ro.DoLayoutList();
@@ -436,9 +461,11 @@ namespace Coffee.UIExtensions
{
if (!p || (ignoreCurrent && target == p)) return;
var cr = p.canvasRenderer;
DestroyImmediate(p);
DestroyImmediate(cr);
Misc.DestroyImmediate(p);
if (p.TryGetComponent<CanvasRenderer>(out var cr))
{
Misc.DestroyImmediate(cr);
}
#if UNITY_2018_3_OR_NEWER
var stage = PrefabStageUtility.GetCurrentPrefabStage();

View File

Before

Width:  |  Height:  |  Size: 418 B

After

Width:  |  Height:  |  Size: 418 B

View File

@@ -1,9 +1,9 @@
# <img alt="UIParticleIcon" src="https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/d76e105e-a840-4f61-a1f6-8cf311c0812d" width="26"/> Particle Effect For UGUI (UI Particle) <!-- omit in toc -->
# <img alt="UIParticleIcon" src="https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/d76e105e-a840-4f61-a1f6-8cf311c0812d" width="26"/> UI Particle v5 <!-- omit in toc -->
[![](https://img.shields.io/npm/v/com.coffee.ui-particle?label=openupm&registry_uri=https://package.openupm.com)](https://openupm.com/packages/com.coffee.ui-particle/)
[![](https://img.shields.io/github/v/release/mob-sakai/ParticleEffectForUGUI)](https://github.com/mob-sakai/ParticleEffectForUGUI/releases)
[![](https://img.shields.io/github/license/mob-sakai/ParticleEffectForUGUI.svg)](https://github.com/mob-sakai/ParticleEffectForUGUI/blob/main/LICENSE.md)
![](https://img.shields.io/badge/Unity-2018.2+-57b9d3.svg?style=flat&logo=unity)
![](https://img.shields.io/badge/Unity-2019.3+-57b9d3.svg?style=flat&logo=unity)
![](https://img.shields.io/badge/uGUI_2.0_Ready-57b9d3.svg?style=flat)
![](https://img.shields.io/badge/UPR%2FHDPR_Ready-57b9d3.svg?style=flat)
![](https://github.com/mob-sakai/ParticleEffectForUGUI/actions/workflows/test.yml/badge.svg?branch=develop)
@@ -11,7 +11,7 @@
[![](https://img.shields.io/github/watchers/mob-sakai/ParticleEffectForUGUI.svg?style=social&label=Watch)](https://github.com/mob-sakai/ParticleEffectForUGUI/subscription)
[![](https://img.shields.io/twitter/follow/mob_sakai.svg?label=Follow&style=social)](https://twitter.com/intent/follow?screen_name=mob_sakai)
<< [📝 Description](#-description-) | [📌 Key Features](#-key-features) | [🎮 Demo](#-demo) | [⚙ Installation](#-installation) | [🚀 Usage](#-usage) | [🛠 Development Note](#-development-note) | [🤝 Contributing](#-contributing) >>
<< [📝 Description](#-description-) | [📌 Key Features](#-key-features) | [🎮 Demo](#-demo) | [⚙ Installation](#-installation) | [🔄 Upgrading to 5.x](#-upgrading-from-3x4x-to-5x) | [🚀 Usage](#-usage) | [🛠 Development Note](#-development-note) | [🤝 Contributing](#-contributing) >>
## 📝 Description <!-- omit in toc -->
@@ -26,7 +26,9 @@ You can render, mask, and sort your `ParticleSystems` for UI without the need fo
- [Install via OpenUPM](#install-via-openupm)
- [Install via UPM (with Package Manager UI)](#install-via-upm-with-package-manager-ui)
- [Install via UPM (Manually)](#install-via-upm-manually)
- [Install as Embedded Package](#install-as-embedded-package)
- [Install as Embedded Package](#install-as-embedded-package)
- [🔄 Upgrading from v3/v4 to v5](#-upgrading-from-v3v4-to-v5)
- [Breaking Changes](#breaking-changes)
- [🚀 Usage](#-usage)
- [Component: UIParticle](#component-uiparticle)
- [Basic Usage](#basic-usage)
@@ -34,6 +36,7 @@ You can render, mask, and sort your `ParticleSystems` for UI without the need fo
- [Usage with `Mask` or `RectMask2D` Component](#usage-with-mask-or-rectmask2d-component)
- [Usage with Script](#usage-with-script)
- [Component: UIParticleAttractor](#component-uiparticleattractor)
- [Project Settings](#project-settings)
- [🛠 Development Note](#-development-note)
- [Compares the Baking mesh approach with the conventional approach](#compares-the-baking-mesh-approach-with-the-conventional-approach)
- [Performance test results](#performance-test-results)
@@ -103,7 +106,7 @@ You can render, mask, and sort your `ParticleSystems` for UI without the need fo
## ⚙ Installation
_This package requires **Unity 2018.3 or later**._
_This package requires **Unity 2019.3 or later**._
#### Install via OpenUPM
@@ -115,16 +118,16 @@ _This package requires **Unity 2018.3 or later**._
```
- To update the package, use Package Manager UI (`Window > Package Manager`) or run the following command with `@{version}`:
```
openupm add com.coffee.ui-particle@4.9.0
openupm add com.coffee.ui-particle@5.0.0
```
#### Install via UPM (with Package Manager UI)
- Click `Window > Package Manager` to open Package Manager UI.
- Click `+ > Add package from git URL...` and input the repository URL: `https://github.com/mob-sakai/ParticleEffectForUGUI.git`
- Click `+ > Add package from git URL...` and input the repository URL: `https://github.com/mob-sakai/ParticleEffectForUGUI.git?path=Packages/src`
![](https://github.com/user-attachments/assets/f88f47ad-c606-44bd-9e86-ee3f72eac548)
- To update the package, change suffix `#{version}` to the target version.
- e.g. `https://github.com/mob-sakai/ParticleEffectForUGUI.git#4.9.0`
- e.g. `https://github.com/mob-sakai/ParticleEffectForUGUI.git?path=Packages/src#5.0.0`
#### Install via UPM (Manually)
@@ -132,22 +135,41 @@ _This package requires **Unity 2018.3 or later**._
```json
{
"dependencies": {
"com.coffee.ui-particle": "https://github.com/mob-sakai/ParticleEffectForUGUI.git",
"com.coffee.ui-particle": "https://github.com/mob-sakai/ParticleEffectForUGUI.git?path=Packages/src",
...
}
}
```
- To update the package, change suffix `#{version}` to the target version.
- e.g. `"com.coffee.ui-particle": "https://github.com/mob-sakai/ParticleEffectForUGUI.git#4.9.0",`
- e.g. `"com.coffee.ui-particle": "https://github.com/mob-sakai/ParticleEffectForUGUI.git?path=Packages/src#5.0.0",`
#### Install as Embedded Package
### Install as Embedded Package
1. Download a source code zip file from [Releases](https://github.com/mob-sakai/ParticleEffectForUGUI.git/releases) and extract it.
2. Place it in your project's `Packages` directory.
![](https://github.com/mob-sakai/mob-sakai/assets/12690315/0b7484b4-5fca-43b0-a9ef-e5dbd99bcdb4)
- If you want to fix bugs or add features, install it as an embedded package.
- To update the package, you need to re-download it and replace the contents.
1. Download the `Source code (zip)` file from [Releases](https://github.com/mob-sakai/SoftMaskForUGUI/releases) and
extract it.
2. Move the `<extracted_dir>/Packages/src` directory into your project's `Packages` directory.
![](https://github.com/user-attachments/assets/187cbcbe-5922-4ed5-acec-cf19aa17d208)
- You can rename the `src` directory if needed.
- If you intend to fix bugs or add features, installing it as an embedded package is recommended.
- To update the package, re-download it and replace the existing contents.
<br><br>
## 🔄 Upgrading from v3/v4 to v5
### Breaking Changes
- The default value of `UIParticle.scale` has been changed from `10` to `1`.
- `UIParticle` no longer inherits from `MaskableGraphic`.
- If you are installing via git URL, add `?path=Packages/src`.
```json
// v3/v4
"com.coffee.ui-particle": "https://github.com/mob-sakai/ParticleEffectForUGUI.git",
// v5
"com.coffee.ui-particle": "https://github.com/mob-sakai/ParticleEffectForUGUI.git?path=Packages/src",
```
<br><br>
@@ -157,7 +179,7 @@ _This package requires **Unity 2018.3 or later**._
`UIParticle` controls the ParticleSystems that are attached to its own game objects and child game objects.
![](https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/1cf5753b-33fc-4cef-91c3-413c515a954f)
![](https://github.com/user-attachments/assets/bc9eb783-afce-4102-ac61-aee9ea8d6f2f)
- **Maskable**: Does this graphic allow maskable.
- **Scale**: Scale the rendering particles. When the `3D` toggle is enabled, 3D scale (x, y, z) is supported.
@@ -179,6 +201,7 @@ _This package requires **Unity 2018.3 or later**._
- **UIParticle:** UIParticle.scale will be adjusted.
- **Use Custom View:** Use this if the particles are not displayed correctly due to min/max particle size.
- **Custom view size:** Change the bake view size.
- **Time Scale Multiplier:** Time scale multiplier.
- **Rendering Order**: The ParticleSystem list to be rendered. You can change the order and the materials.
**NOTE:** Press the `Refresh` button to reconstruct the rendering order based on children ParticleSystem's sorting order
@@ -209,7 +232,7 @@ and z-position.
If you want to mask particles, set a stencil-supported shader (such as `UI/UIAdditive`) to the material for
ParticleSystem.
If you use some custom shaders, see
the [How to Make a Custom Shader to Support Mask/RectMask2D Component](#how-to-make-a-custom-shader-to-support-maskrectmask2d-component)
the [How to Make a Custom Shader to Support Mask/RectMask2D Component](#how-to-make-a-custom-shader-to-support-mask-and-rectmask2d-component)
section.
![](https://user-images.githubusercontent.com/12690315/95017591-3b512700-0695-11eb-864e-04166ea1809a.png)
@@ -256,6 +279,14 @@ uiParticle.Stop();
<br><br>
### Project Settings
![](https://github.com/user-attachments/assets/befc7f34-fb47-4006-831a-eba79fda11ca)
- Click `Edit > Project Settings` to open the Project Settings window and then select `UI > UI Particle` category.
<br><br>
## 🛠 Development Note
### Compares the Baking mesh approach with the conventional approach

Binary file not shown.

View File

@@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 4d73b3825bf044d418ae21bb331d3902
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 7a55e246f37df405bac88eac692e3a86
guid: 53aa3f36032944b3fb1455e774c52396
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -1,6 +1,7 @@
fileFormatVersion: 2
guid: b73940fc30a2f4eb9a73783e9c1f8da6
PrefabImporter:
guid: 8cf8018dee45a4c42a19eec890eaa5b1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:

View File

@@ -0,0 +1,134 @@
#if UNITY_2021_3_0 || UNITY_2021_3_1 || UNITY_2021_3_2 || UNITY_2021_3_3 || UNITY_2021_3_4 || UNITY_2021_3_5 || UNITY_2021_3_6 || UNITY_2021_3_7 || UNITY_2021_3_8 || UNITY_2021_3_9
#elif UNITY_2021_3_10 || UNITY_2021_3_11 || UNITY_2021_3_12 || UNITY_2021_3_13 || UNITY_2021_3_14 || UNITY_2021_3_15 || UNITY_2021_3_16 || UNITY_2021_3_17 || UNITY_2021_3_18 || UNITY_2021_3_19
#elif UNITY_2021_3_20 || UNITY_2021_3_21 || UNITY_2021_3_22 || UNITY_2021_3_23 || UNITY_2021_3_24 || UNITY_2021_3_25 || UNITY_2021_3_26 || UNITY_2021_3_27 || UNITY_2021_3_28 || UNITY_2021_3_29
#elif UNITY_2021_3_30 || UNITY_2021_3_31 || UNITY_2021_3_32 || UNITY_2021_3_33
#elif UNITY_2022_2_0 || UNITY_2022_2_1 || UNITY_2022_2_2 || UNITY_2022_2_3 || UNITY_2022_2_4 || UNITY_2022_2_5 || UNITY_2022_2_6 || UNITY_2022_2_7 || UNITY_2022_2_8 || UNITY_2022_2_9
#elif UNITY_2022_2_10 || UNITY_2022_2_11 || UNITY_2022_2_12 || UNITY_2022_2_13 || UNITY_2022_2_14
#elif UNITY_2021_3 || UNITY_2022_2 || UNITY_2022_3 || UNITY_2023_2_OR_NEWER
#define CANVAS_SUPPORT_ALWAYS_GAMMA
#endif
using UnityEngine;
using UnityEngine.Profiling;
#if UNITY_MODULE_VR
using UnityEngine.XR;
#endif
namespace Coffee.UIParticleInternal
{
internal static class CanvasExtensions
{
public static bool ShouldGammaToLinearInShader(this Canvas canvas)
{
return QualitySettings.activeColorSpace == ColorSpace.Linear &&
#if CANVAS_SUPPORT_ALWAYS_GAMMA
canvas.vertexColorAlwaysGammaSpace;
#else
false;
#endif
}
public static bool ShouldGammaToLinearInMesh(this Canvas canvas)
{
return QualitySettings.activeColorSpace == ColorSpace.Linear &&
#if CANVAS_SUPPORT_ALWAYS_GAMMA
!canvas.vertexColorAlwaysGammaSpace;
#else
true;
#endif
}
public static bool IsStereoCanvas(this Canvas canvas)
{
#if UNITY_MODULE_VR
if (FrameCache.TryGet<bool>(canvas, nameof(IsStereoCanvas), out var stereo)) return stereo;
stereo =
canvas != null && canvas.renderMode != RenderMode.ScreenSpaceOverlay && canvas.worldCamera != null
&& XRSettings.enabled && !string.IsNullOrEmpty(XRSettings.loadedDeviceName);
FrameCache.Set(canvas, nameof(IsStereoCanvas), stereo);
return stereo;
#else
return false;
#endif
}
/// <summary>
/// Gets the view-projection matrix for a Canvas.
/// </summary>
public static void GetViewProjectionMatrix(this Canvas canvas, out Matrix4x4 vpMatrix)
{
canvas.GetViewProjectionMatrix(Camera.MonoOrStereoscopicEye.Mono, out vpMatrix);
}
/// <summary>
/// Gets the view-projection matrix for a Canvas.
/// </summary>
public static void GetViewProjectionMatrix(this Canvas canvas, Camera.MonoOrStereoscopicEye eye,
out Matrix4x4 vpMatrix)
{
if (FrameCache.TryGet(canvas, nameof(GetViewProjectionMatrix), out vpMatrix)) return;
canvas.GetViewProjectionMatrix(eye, out var viewMatrix, out var projectionMatrix);
vpMatrix = viewMatrix * projectionMatrix;
FrameCache.Set(canvas, nameof(GetViewProjectionMatrix), vpMatrix);
}
/// <summary>
/// Gets the view and projection matrices for a Canvas.
/// </summary>
public static void GetViewProjectionMatrix(this Canvas canvas, out Matrix4x4 vMatrix, out Matrix4x4 pMatrix)
{
canvas.GetViewProjectionMatrix(Camera.MonoOrStereoscopicEye.Mono, out vMatrix, out pMatrix);
}
/// <summary>
/// Gets the view and projection matrices for a Canvas.
/// </summary>
public static void GetViewProjectionMatrix(this Canvas canvas, Camera.MonoOrStereoscopicEye eye,
out Matrix4x4 vMatrix, out Matrix4x4 pMatrix)
{
if (FrameCache.TryGet(canvas, "GetViewMatrix", (int)eye, out vMatrix) &&
FrameCache.TryGet(canvas, "GetProjectionMatrix", (int)eye, out pMatrix))
{
return;
}
// Get view and projection matrices.
Profiler.BeginSample("(COF)[CanvasExt] GetViewProjectionMatrix");
var rootCanvas = canvas.rootCanvas;
var cam = rootCanvas.worldCamera;
if (rootCanvas && rootCanvas.renderMode != RenderMode.ScreenSpaceOverlay && cam)
{
if (eye == Camera.MonoOrStereoscopicEye.Mono)
{
vMatrix = cam.worldToCameraMatrix;
pMatrix = GL.GetGPUProjectionMatrix(cam.projectionMatrix, false);
}
else
{
pMatrix = cam.GetStereoProjectionMatrix((Camera.StereoscopicEye)eye);
vMatrix = cam.GetStereoViewMatrix((Camera.StereoscopicEye)eye);
pMatrix = GL.GetGPUProjectionMatrix(pMatrix, false);
}
}
else
{
var pos = rootCanvas.transform.position;
vMatrix = Matrix4x4.TRS(
new Vector3(-pos.x, -pos.y, -1000),
Quaternion.identity,
new Vector3(1, 1, -1f));
pMatrix = Matrix4x4.TRS(
new Vector3(0, 0, -1),
Quaternion.identity,
new Vector3(1 / pos.x, 1 / pos.y, -2 / 10000f));
}
FrameCache.Set(canvas, "GetViewMatrix", (int)eye, vMatrix);
FrameCache.Set(canvas, "GetProjectionMatrix", (int)eye, pMatrix);
Profiler.EndSample();
}
}
}

View File

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

View File

@@ -0,0 +1,77 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
namespace Coffee.UIParticleInternal
{
internal static class Color32Extensions
{
private static readonly List<Color32> s_Colors = new List<Color32>();
private static byte[] s_LinearToGammaLut;
private static byte[] s_GammaToLinearLut;
public static byte LinearToGamma(this byte self)
{
if (s_LinearToGammaLut == null)
{
s_LinearToGammaLut = new byte[256];
for (var i = 0; i < 256; i++)
{
s_LinearToGammaLut[i] = (byte)(Mathf.LinearToGammaSpace(i / 255f) * 255f);
}
}
return s_LinearToGammaLut[self];
}
public static byte GammaToLinear(this byte self)
{
if (s_GammaToLinearLut == null)
{
s_GammaToLinearLut = new byte[256];
for (var i = 0; i < 256; i++)
{
s_GammaToLinearLut[i] = (byte)(Mathf.GammaToLinearSpace(i / 255f) * 255f);
}
}
return s_GammaToLinearLut[self];
}
public static void LinearToGamma(this Mesh self)
{
Profiler.BeginSample("(COF)[ColorExt] LinearToGamma (Mesh)");
self.GetColors(s_Colors);
var count = s_Colors.Count;
for (var i = 0; i < count; i++)
{
var c = s_Colors[i];
c.r = c.r.LinearToGamma();
c.g = c.g.LinearToGamma();
c.b = c.b.LinearToGamma();
s_Colors[i] = c;
}
self.SetColors(s_Colors);
Profiler.EndSample();
}
public static void GammaToLinear(this Mesh self)
{
Profiler.BeginSample("(COF)[ColorExt] GammaToLinear (Mesh)");
self.GetColors(s_Colors);
var count = s_Colors.Count;
for (var i = 0; i < count; i++)
{
var c = s_Colors[i];
c.r = c.r.GammaToLinear();
c.g = c.g.GammaToLinear();
c.b = c.b.GammaToLinear();
s_Colors[i] = c;
}
self.SetColors(s_Colors);
Profiler.EndSample();
}
}
}

View File

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

View File

@@ -0,0 +1,227 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.Profiling;
using Object = UnityEngine.Object;
namespace Coffee.UIParticleInternal
{
/// <summary>
/// Extension methods for Component class.
/// </summary>
internal static class ComponentExtensions
{
/// <summary>
/// Get components in children of a specific type in the hierarchy of a GameObject.
/// </summary>
public static T[] GetComponentsInChildren<T>(this Component self, int depth)
where T : Component
{
var results = InternalListPool<T>.Rent();
self.GetComponentsInChildren_Internal(results, depth);
var array = results.ToArray();
InternalListPool<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_Internal(results, depth - 1);
}
}
/// <summary>
/// Get or add a component of a specific type to a GameObject.
/// </summary>
public static T GetOrAddComponent<T>(this Component self) where T : Component
{
if (!self) return null;
return self.TryGetComponent<T>(out var component)
? component
: self.gameObject.AddComponent<T>();
}
/// <summary>
/// Get the root component of a specific type in the hierarchy of a GameObject.
/// </summary>
public static T GetRootComponent<T>(this Component self) where T : Component
{
T component = null;
var transform = self.transform;
while (transform)
{
if (transform.TryGetComponent<T>(out var c))
{
component = c;
}
transform = transform.parent;
}
return component;
}
/// <summary>
/// Get a component of a specific type in the parent hierarchy of a GameObject.
/// </summary>
public static T GetComponentInParent<T>(this Component self, bool includeSelf, Transform stopAfter,
Predicate<T> valid)
where T : Component
{
var tr = includeSelf ? self.transform : self.transform.parent;
while (tr)
{
if (tr.TryGetComponent<T>(out var c) && valid(c)) return c;
if (tr == stopAfter) return null;
tr = tr.parent;
}
return null;
}
/// <summary>
/// Add a component of a specific type to the children of a GameObject.
/// </summary>
public static void AddComponentOnChildren<T>(this Component self, HideFlags hideFlags, bool includeSelf)
where T : Component
{
if (self == null) return;
Profiler.BeginSample("(COF)[ComponentExt] AddComponentOnChildren > Self");
if (includeSelf && !self.TryGetComponent<T>(out _))
{
var c = self.gameObject.AddComponent<T>();
c.hideFlags = hideFlags;
}
Profiler.EndSample();
Profiler.BeginSample("(COF)[ComponentExt] AddComponentOnChildren > Child");
var childCount = self.transform.childCount;
for (var i = 0; i < childCount; i++)
{
var child = self.transform.GetChild(i);
if (child.TryGetComponent<T>(out _)) continue;
var c = child.gameObject.AddComponent<T>();
c.hideFlags = hideFlags;
}
Profiler.EndSample();
}
/// <summary>
/// Add a component of a specific type to the children of a GameObject.
/// </summary>
public static void AddComponentOnChildren<T>(this Component self, bool includeSelf)
where T : Component
{
if (self == null) return;
Profiler.BeginSample("(COF)[ComponentExt] AddComponentOnChildren > Self");
if (includeSelf && !self.TryGetComponent<T>(out _))
{
self.gameObject.AddComponent<T>();
}
Profiler.EndSample();
Profiler.BeginSample("(COF)[ComponentExt] AddComponentOnChildren > Child");
var childCount = self.transform.childCount;
for (var i = 0; i < childCount; i++)
{
var child = self.transform.GetChild(i);
if (child.TryGetComponent<T>(out _)) continue;
child.gameObject.AddComponent<T>();
}
Profiler.EndSample();
}
#if !UNITY_2021_2_OR_NEWER && !UNITY_2020_3_45 && !UNITY_2020_3_46 && !UNITY_2020_3_47 && !UNITY_2020_3_48
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 MonoImporter.GetAllRuntimeMonoScripts())
{
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

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

View File

@@ -0,0 +1,60 @@
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 Func<Sprite, Texture2D> s_GetActiveAtlasTextureMethod =
(Func<Sprite, Texture2D>)Delegate.CreateDelegate(typeof(Func<Sprite, Texture2D>),
s_SpriteEditorExtensionType
.GetMethod("GetActiveAtlasTexture", BindingFlags.Static | BindingFlags.NonPublic));
private static readonly Func<Sprite, SpriteAtlas> s_GetActiveAtlasMethod =
(Func<Sprite, SpriteAtlas>)Delegate.CreateDelegate(typeof(Func<Sprite, SpriteAtlas>),
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;
var ret = s_GetActiveAtlasTextureMethod(self);
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(self);
}
#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

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

View File

@@ -0,0 +1,46 @@
using UnityEngine;
namespace Coffee.UIParticleInternal
{
internal static class Vector3Extensions
{
public static Vector3 Inverse(this Vector3 self)
{
self.x = Mathf.Approximately(self.x, 0) ? 1 : 1 / self.x;
self.y = Mathf.Approximately(self.y, 0) ? 1 : 1 / self.y;
self.z = Mathf.Approximately(self.z, 0) ? 1 : 1 / self.z;
return self;
}
public static Vector3 GetScaled(this Vector3 self, Vector3 other1)
{
self.Scale(other1);
return self;
}
public static Vector3 GetScaled(this Vector3 self, Vector3 other1, Vector3 other2)
{
self.Scale(other1);
self.Scale(other2);
return self;
}
public static Vector3 GetScaled(this Vector3 self, Vector3 other1, Vector3 other2, Vector3 other3)
{
self.Scale(other1);
self.Scale(other2);
self.Scale(other3);
return self;
}
public static bool IsVisible(this Vector3 self)
{
return 0 < Mathf.Abs(self.x * self.y * self.z);
}
public static bool IsVisible2D(this Vector3 self)
{
return 0 < Mathf.Abs(self.x * self.y);
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,242 @@
using System;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
#if UNITY_EDITOR
using System.IO;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
#endif
namespace Coffee.UIParticleInternal
{
public abstract class PreloadedProjectSettings : ScriptableObject
#if UNITY_EDITOR
{
private class Postprocessor : AssetPostprocessor
{
private static void OnPostprocessAllAssets(string[] _, string[] __, string[] ___, string[] ____)
{
Initialize();
}
}
private class PreprocessBuildWithReport : IPreprocessBuildWithReport
{
int IOrderedCallback.callbackOrder => 0;
void IPreprocessBuildWithReport.OnPreprocessBuild(BuildReport report)
{
Initialize();
}
}
private static void Initialize()
{
foreach (var t in TypeCache.GetTypesDerivedFrom(typeof(PreloadedProjectSettings<>)))
{
var defaultSettings = GetDefaultSettings(t);
if (!defaultSettings)
{
// When create a new instance, automatically set it as default settings.
defaultSettings = CreateInstance(t) as PreloadedProjectSettings;
SetDefaultSettings(defaultSettings);
}
else if (GetPreloadedSettings(t).Length != 1)
{
SetDefaultSettings(defaultSettings);
}
if (defaultSettings)
{
defaultSettings.OnInitialize();
}
}
}
protected static string GetDefaultName(Type type, bool nicify)
{
var typeName = type.Name;
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)
{
if (!asset) return;
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);
if (!File.Exists(assetPath))
{
AssetDatabase.CreateAsset(asset, assetPath);
asset.OnCreateAsset();
}
}
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();
}
protected virtual void OnCreateAsset()
{
}
protected virtual void OnInitialize()
{
}
}
#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 bool hasInstance => s_Instance;
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

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

View File

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

View File

@@ -0,0 +1,89 @@
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 InternalObjectPool<LinkedListNode<T>> s_NodePool =
new InternalObjectPool<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)
{
if (rhs == null) return;
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)
{
if (rhs == null) return;
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;
}
}
public void Clear()
{
_delegates.Clear();
}
}
/// <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

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

View File

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

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

View File

@@ -0,0 +1,254 @@
using System;
using System.Text;
using UnityEngine;
using Object = UnityEngine.Object;
#if ENABLE_COFFEE_LOGGER
using System.Reflection;
using System.Collections.Generic;
#else
using Conditional = System.Diagnostics.ConditionalAttribute;
#endif
namespace Coffee.UIParticleInternal
{
internal static class Logging
{
#if !ENABLE_COFFEE_LOGGER
private const string k_DisableSymbol = "DISABLE_COFFEE_LOGGER";
[Conditional(k_DisableSymbol)]
#endif
private static void Log_Internal(LogType type, object tag, object message, Object context)
{
#if ENABLE_COFFEE_LOGGER
AppendTag(s_Sb, tag);
s_Sb.Append(message);
switch (type)
{
case LogType.Error:
case LogType.Assert:
case LogType.Exception:
Debug.LogError(s_Sb, context);
break;
case LogType.Warning:
Debug.LogWarning(s_Sb, context);
break;
case LogType.Log:
Debug.Log(s_Sb, context);
break;
}
s_Sb.Length = 0;
#endif
}
#if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)]
#endif
public static void LogIf(bool enable, object tag, object message, Object context = null)
{
if (!enable) return;
Log_Internal(LogType.Log, tag, message, context ? context : tag as Object);
}
#if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)]
#endif
public static void Log(object tag, object message, Object context = null)
{
Log_Internal(LogType.Log, tag, message, context ? context : tag as Object);
}
#if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)]
#endif
public static void LogWarning(object tag, object message, Object context = null)
{
Log_Internal(LogType.Warning, tag, message, context ? context : tag as Object);
}
public static void LogError(object tag, object message, Object context = null)
{
#if ENABLE_COFFEE_LOGGER
Log_Internal(LogType.Error, tag, message, context ? context : tag as Object);
#else
Debug.LogError($"{tag}: {message}", context);
#endif
}
#if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)]
#endif
public static void LogMulticast(Type type, string fieldName, object instance = null, string message = null)
{
#if ENABLE_COFFEE_LOGGER
AppendTag(s_Sb, instance ?? type);
var handler = type
.GetField(fieldName,
BindingFlags.Static | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic)
?.GetValue(instance);
var list = ((MulticastDelegate)handler)?.GetInvocationList() ?? Array.Empty<Delegate>();
s_Sb.Append("<color=orange>");
s_Sb.Append(type.Name);
s_Sb.Append(".");
s_Sb.Append(fieldName);
s_Sb.Append(" has ");
s_Sb.Append(list.Length);
s_Sb.Append(" callbacks");
if (message != null)
{
s_Sb.Append(" (");
s_Sb.Append(message);
s_Sb.Append(")");
}
s_Sb.Append(":</color>");
for (var i = 0; i < list.Length; i++)
{
s_Sb.Append("\n - ");
s_Sb.Append(list[i].Method.DeclaringType?.Name);
s_Sb.Append(".");
s_Sb.Append(list[i].Method.Name);
}
Debug.Log(s_Sb);
s_Sb.Length = 0;
#endif
}
#if !ENABLE_COFFEE_LOGGER
[Conditional(k_DisableSymbol)]
#endif
private static void AppendTag(StringBuilder sb, object tag)
{
#if ENABLE_COFFEE_LOGGER
try
{
sb.Append("f");
sb.Append(Time.frameCount);
sb.Append(":<color=#");
AppendReadableCode(sb, tag);
sb.Append("><b>[");
switch (tag)
{
case string name:
sb.Append(name);
break;
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

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

View File

@@ -0,0 +1,92 @@
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)]
public static void Clear()
{
s_Repository.Clear();
}
#endif
/// <summary>
/// Retrieves a cached material based on the hash.
/// </summary>
public static bool Valid(Hash128 hash, Material material)
{
Profiler.BeginSample("(COF)[MaterialRegistry] Valid");
var ret = s_Repository.Valid(hash, material);
Profiler.EndSample();
return ret;
}
/// <summary>
/// Adds or retrieves a cached material based on the hash.
/// </summary>
public static void Get(Hash128 hash, ref Material material, Func<Material> onCreate)
{
Profiler.BeginSample("(COF)[MaterialRepository] Get");
s_Repository.Get(hash, ref material, onCreate);
Profiler.EndSample();
}
/// <summary>
/// Adds or retrieves a cached material based on the hash.
/// </summary>
public static void Get(Hash128 hash, ref Material material, string shaderName)
{
Profiler.BeginSample("(COF)[MaterialRepository] Get");
s_Repository.Get(hash, ref material, x => new Material(Shader.Find(x))
{
hideFlags = HideFlags.DontSave | HideFlags.NotEditable
}, shaderName);
Profiler.EndSample();
}
/// <summary>
/// Adds or retrieves a cached material based on the hash.
/// </summary>
public static void Get(Hash128 hash, ref Material material, string shaderName, string[] keywords)
{
Profiler.BeginSample("(COF)[MaterialRepository] Get");
s_Repository.Get(hash, ref material, x => new Material(Shader.Find(x.shaderName))
{
hideFlags = HideFlags.DontSave | HideFlags.NotEditable,
shaderKeywords = x.keywords
}, (shaderName, keywords));
Profiler.EndSample();
}
/// <summary>
/// Adds or retrieves a cached material based on the hash.
/// </summary>
public static void Get<T>(Hash128 hash, ref Material material, Func<T, Material> onCreate, T source)
{
Profiler.BeginSample("(COF)[MaterialRepository] Get");
s_Repository.Get(hash, ref material, onCreate, source);
Profiler.EndSample();
}
/// <summary>
/// Removes a soft mask material from the cache.
/// </summary>
public static void Release(ref Material material)
{
Profiler.BeginSample("(COF)[MaterialRepository] Release");
s_Repository.Release(ref material);
Profiler.EndSample();
}
}
}

View File

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

View File

@@ -0,0 +1,132 @@
using System;
using System.Diagnostics;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
#if UNITY_EDITOR
using System.IO;
using System.Linq;
using System.Reflection;
#if UNITY_2021_2_OR_NEWER
using UnityEditor.SceneManagement;
#else
using UnityEditor.Experimental.SceneManagement;
#endif
#endif
namespace Coffee.UIParticleInternal
{
internal static class Misc
{
public static T[] FindObjectsOfType<T>() where T : Object
{
#if UNITY_2023_1_OR_NEWER
return Object.FindObjectsByType<T>(FindObjectsInactive.Include, FindObjectsSortMode.None);
#else
return Object.FindObjectsOfType<T>();
#endif
}
public static void Destroy(Object obj)
{
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
}
#if UNITY_EDITOR
public static T[] GetAllComponentsInPrefabStage<T>() where T : Component
{
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
if (prefabStage == null) return Array.Empty<T>();
return prefabStage.prefabContentsRoot.GetComponentsInChildren<T>(true);
}
public static bool isBatchOrBuilding => Application.isBatchMode || BuildPipeline.isBuildingPlayer;
#endif
[Conditional("UNITY_EDITOR")]
public static void QueuePlayerLoopUpdate()
{
#if UNITY_EDITOR
if (!EditorApplication.isPlaying)
{
EditorApplication.QueuePlayerLoopUpdate();
}
#endif
}
}
#if !UNITY_2021_2_OR_NEWER
[AttributeUsage(AttributeTargets.Class)]
[Conditional("UNITY_EDITOR")]
internal class IconAttribute : Attribute
{
private readonly string _path;
public IconAttribute(string path)
{
_path = path;
}
#if UNITY_EDITOR
private static Action<Object, Texture2D> s_SetIconForObject = typeof(EditorGUIUtility)
.GetMethod("SetIconForObject", BindingFlags.Static | BindingFlags.NonPublic)
.CreateDelegate(typeof(Action<Object, Texture2D>), null) as Action<Object, Texture2D>;
[InitializeOnLoadMethod]
private static void InitializeOnLoadMethod()
{
if (Misc.isBatchOrBuilding) return;
var types = TypeCache.GetTypesWithAttribute<IconAttribute>();
var scripts = MonoImporter.GetAllRuntimeMonoScripts();
foreach (var type in types)
{
var script = scripts.FirstOrDefault(x => x.GetClass() == type);
if (!script) continue;
var path = type.GetCustomAttribute<IconAttribute>()?._path;
var icon = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
if (!icon) continue;
s_SetIconForObject(script, icon);
}
}
#endif
}
#endif
}

View File

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

View File

@@ -0,0 +1,154 @@
using System;
using System.Collections.Generic;
namespace Coffee.UIParticleInternal
{
/// <summary>
/// Object pool.
/// </summary>
internal class InternalObjectPool<T> where T : class
{
#if UNITY_2021_1_OR_NEWER
private readonly Predicate<T> _onValid; // Delegate for checking if instances are valid
private readonly UnityEngine.Pool.ObjectPool<T> _pool;
public InternalObjectPool(Func<T> onCreate, Predicate<T> onValid, Action<T> onReturn)
{
_pool = new UnityEngine.Pool.ObjectPool<T>(onCreate, null, onReturn);
_onValid = onValid;
}
/// <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.CountInactive)
{
var instance = _pool.Get();
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.CountInactive}, created: {_pool.CountAll}).");
return _pool.Get();
}
/// <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) return; // Ignore if already pooled or null.
_pool.Release(instance);
Logging.Log(this, $"An instance is released (pooled: {_pool.CountInactive}, created: {_pool.CountAll}).");
instance = default; // Set the reference to null.
}
#else
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 InternalObjectPool(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.
}
#endif
}
/// <summary>
/// Object pool for <see cref="List{T}" />.
/// </summary>
internal static class InternalListPool<T>
{
#if UNITY_2021_1_OR_NEWER
/// <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 UnityEngine.Pool.ListPool<T>.Get();
}
/// <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)
{
if (toRelease != null)
{
UnityEngine.Pool.ListPool<T>.Release(toRelease);
}
toRelease = null;
}
#else
private static readonly InternalObjectPool<List<T>> s_ListPool =
new InternalObjectPool<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);
}
#endif
}
}

View File

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

View File

@@ -0,0 +1,209 @@
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 Dictionary<Hash128, Entry> _cache = new Dictionary<Hash128, Entry>(8);
private readonly Dictionary<int, Hash128> _objectKey = new Dictionary<int, Hash128>(8);
private readonly string _name;
private readonly Action<T> _onRelease;
private readonly Stack<Entry> _pool = new Stack<Entry>(8);
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;
}
for (var i = 0; i < 8; i++)
{
_pool.Push(new Entry());
}
}
public int count => _cache.Count;
public void Clear()
{
foreach (var kv in _cache)
{
var entry = kv.Value;
if (entry == null) continue;
entry.Release(_onRelease);
_pool.Push(entry);
}
_cache.Clear();
_objectKey.Clear();
}
public bool Valid(Hash128 hash, T obj)
{
return _cache.TryGetValue(hash, out var entry) && entry.storedObject == obj;
}
/// <summary>
/// Adds or retrieves a cached object based on the hash.
/// </summary>
public void Get(Hash128 hash, ref T obj, Func<T> onCreate)
{
if (GetFromCache(hash, ref obj)) return;
Add(hash, ref obj, onCreate());
}
/// <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)
{
if (GetFromCache(hash, ref obj)) return;
Add(hash, ref obj, onCreate(source));
}
private bool GetFromCache(Hash128 hash, ref T obj)
{
// Find existing entry.
Profiler.BeginSample("(COF)[ObjectRepository] GetFromCache");
if (_cache.TryGetValue(hash, out var entry))
{
if (!entry.storedObject)
{
Release(ref entry.storedObject);
Profiler.EndSample();
return false;
}
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(total#{count}): {entry}");
}
Profiler.EndSample();
return true;
}
Profiler.EndSample();
return false;
}
private void Add(Hash128 hash, ref T obj, T newObject)
{
if (!newObject)
{
Release(ref obj);
obj = newObject;
return;
}
// Create and add a new entry.
Profiler.BeginSample("(COF)[ObjectRepository] Add");
var newEntry = 0 < _pool.Count ? _pool.Pop() : new Entry();
newEntry.storedObject = newObject;
newEntry.hash = hash;
newEntry.reference = 1;
_cache[hash] = newEntry;
_objectKey[newObject.GetInstanceID()] = hash;
Logging.Log(_name, $"<color=#03c700>Add</color>(total#{count}): {newEntry}");
Release(ref obj);
obj = newObject;
Profiler.EndSample();
}
/// <summary>
/// Release a object.
/// </summary>
public void Release(ref T obj)
{
if (ReferenceEquals(obj, null)) return;
// Find and release the entry.
Profiler.BeginSample("(COF)[ObjectRepository] Release");
var id = obj.GetInstanceID();
if (_objectKey.TryGetValue(id, out var hash)
&& _cache.TryGetValue(hash, out var entry))
{
entry.reference--;
if (entry.reference <= 0 || !entry.storedObject)
{
Remove(entry);
}
else
{
Logging.Log(_name, $"Release(total#{_cache.Count}): {entry}");
}
}
else
{
Logging.Log(_name, $"Release(total#{_cache.Count}): <color=red>Already released: {obj}</color>");
}
obj = null;
Profiler.EndSample();
}
private void Remove(Entry entry)
{
if (ReferenceEquals(entry, null)) return;
Profiler.BeginSample("(COF)[ObjectRepository] Remove");
_cache.Remove(entry.hash);
_objectKey.Remove(entry.storedObject.GetInstanceID());
_pool.Push(entry);
entry.reference = 0;
Logging.Log(_name, $"<color=#f29e03>Remove</color>(total#{_cache.Count}): {entry}");
entry.Release(_onRelease);
Profiler.EndSample();
}
private class Entry
{
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()} (refs#{reference}), {storedObject}";
}
}
}
}

View File

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

View File

@@ -0,0 +1,115 @@
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();
private static readonly FastAction s_OnScreenSizeChangedAction = new FastAction();
private static Vector2Int s_LastScreenSize;
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>
/// Event that occurs when the screen size changes.
/// </summary>
public static event Action onScreenSizeChanged
{
add => s_OnScreenSizeChangedAction.Add(value);
remove => s_OnScreenSizeChangedAction.Remove(value);
}
/// <summary>
/// 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]
#endif
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void InitializeOnLoad()
{
Canvas.willRenderCanvases -= OnAfterCanvasRebuild;
s_IsInitializedAfterCanvasRebuild = false;
}
/// <summary>
/// Callback method called before canvas rebuilds.
/// </summary>
private static void OnBeforeCanvasRebuild()
{
var screenSize = new Vector2Int(Screen.width, Screen.height);
if (s_LastScreenSize != screenSize)
{
if (s_LastScreenSize != default)
{
s_OnScreenSizeChangedAction.Invoke();
}
s_LastScreenSize = screenSize;
}
s_BeforeCanvasRebuildAction.Invoke();
InitializeAfterCanvasRebuild();
}
/// <summary>
/// Callback method called after canvas rebuilds.
/// </summary>
private static void OnAfterCanvasRebuild()
{
s_AfterCanvasRebuildAction.Invoke();
s_LateAfterCanvasRebuildAction.Invoke();
}
}
}

View File

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

View File

@@ -1,72 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
namespace Coffee.UIParticleExtensions
{
internal class ModifiedMaterial
{
private static readonly List<MatEntry> s_Entries = new List<MatEntry>();
public static Material Add(Material baseMat, Texture texture, int id, int props)
{
MatEntry e;
for (var i = 0; i < s_Entries.Count; i++)
{
e = s_Entries[i];
if (e.baseMat != baseMat || e.texture != texture || e.id != id || e.props != props) continue;
++e.count;
return e.customMat;
}
e = new MatEntry
{
count = 1,
baseMat = baseMat,
texture = texture,
id = id,
props = props,
customMat = new Material(baseMat)
{
name = $"{baseMat.name}_{id}",
hideFlags = HideFlags.DontSave | HideFlags.NotEditable,
mainTexture = texture ? texture : baseMat.mainTexture
}
};
s_Entries.Add(e);
//Debug.LogFormat(">>>> ModifiedMaterial.Add -> count = count:{0}, mat:{1}, tex:{2}, id:{3}", s_Entries.Count, baseMat, texture, id);
return e.customMat;
}
public static void Remove(Material customMat)
{
if (!customMat) return;
for (var i = 0; i < s_Entries.Count; ++i)
{
var e = s_Entries[i];
if (e.customMat != customMat) continue;
if (--e.count == 0)
{
//Debug.LogFormat(">>>> ModifiedMaterial.Remove -> count:{0}, mat:{1}, tex:{2}, id:{3}", s_Entries.Count - 1, e.customMat, e.texture, e.id);
Misc.DestroyImmediate(e.customMat);
e.customMat = null;
e.baseMat = null;
e.texture = null;
s_Entries.RemoveAt(i);
}
break;
}
}
private class MatEntry
{
public Material baseMat;
public int count;
public Material customMat;
public int id;
public int props;
public Texture texture;
}
}
}

View File

@@ -1,24 +1,27 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Coffee.UIParticleExtensions;
using Coffee.UIParticleInternal;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Rendering;
using UnityEngine.Serialization;
using UnityEngine.UI;
using Random = UnityEngine.Random;
[assembly: InternalsVisibleTo("Coffee.UIParticle.Editor")]
[assembly: InternalsVisibleTo("Coffee.UIParticle.PerformanceDemo")]
[assembly: InternalsVisibleTo("Coffee.UIParticle.Demo")]
namespace Coffee.UIExtensions
{
/// <summary>
/// Render maskable and sortable particle effect ,without Camera, RenderTexture or Canvas.
/// </summary>
[Icon("Packages/com.coffee.ui-particle/Icons/UIParticleIcon.png")]
[ExecuteAlways]
[RequireComponent(typeof(RectTransform))]
[RequireComponent(typeof(CanvasRenderer))]
public class UIParticle : MaskableGraphic, ISerializationCallbackReceiver
public class UIParticle : UIBehaviour, ISerializationCallbackReceiver
{
public enum AutoScalingMode
{
@@ -60,7 +63,7 @@ namespace Coffee.UIExtensions
[Tooltip("Scale the rendering particles. When the `3D` toggle is enabled, 3D scale (x, y, z) is supported.")]
[SerializeField]
private Vector3 m_Scale3D = new Vector3(10, 10, 10);
private Vector3 m_Scale3D = new Vector3(1, 1, 1);
[Tooltip("If you want to update material properties (e.g. _MainTex_ST, _Color) in AnimationClip, " +
"use this to mark as animatable.")]
@@ -117,6 +120,13 @@ namespace Coffee.UIExtensions
"Change the bake view size.")]
private float m_CustomViewSize = 10;
[SerializeField]
[Tooltip("Time scale multiplier.")]
private float m_TimeScaleMultiplier = 1;
[SerializeField]
private bool m_Maskable = true;
private readonly List<UIParticleRenderer> _renderers = new List<UIParticleRenderer>();
private Camera _bakeCamera;
private Canvas _canvas;
@@ -125,13 +135,37 @@ namespace Coffee.UIExtensions
private Vector3 _storedScale;
private DrivenRectTransformTracker _tracker;
/// <summary>
/// Should this graphic be considered a target for ray-casting?
/// </summary>
public override bool raycastTarget
public RectTransform rectTransform => transform as RectTransform;
public Canvas canvas
{
get => false;
set { }
get
{
if (_canvas) return _canvas;
var tr = transform;
while (tr && !_canvas)
{
if (tr.TryGetComponent(out _canvas)) return _canvas;
tr = tr.parent;
}
return null;
}
}
/// <summary>
/// Does this graphic allow masking.
/// </summary>
public bool maskable
{
get => m_Maskable;
set
{
if (value == m_Maskable) return;
m_Maskable = value;
UpdateRendererMaterial();
}
}
/// <summary>
@@ -256,6 +290,15 @@ namespace Coffee.UIExtensions
set => m_CustomViewSize = Mathf.Max(0.1f, value);
}
/// <summary>
/// Time scale multiplier.
/// </summary>
public float timeScaleMultiplier
{
get => m_TimeScaleMultiplier;
set => m_TimeScaleMultiplier = value;
}
internal bool useMeshSharing => m_MeshSharing != MeshSharing.None;
internal bool isPrimary =>
@@ -308,15 +351,15 @@ namespace Coffee.UIExtensions
public Vector3 parentScale { get; private set; }
public Vector3 canvasScale { get; private set; }
private Vector3 canvasScale { get; set; }
protected override void OnEnable()
{
_isScaleStored = false;
ResetGroupId();
UIParticleUpdater.Register(this);
RegisterDirtyMaterialCallback(UpdateRendererMaterial);
//
if (0 < particles.Count)
{
RefreshParticles(particles);
@@ -326,7 +369,7 @@ namespace Coffee.UIExtensions
RefreshParticles();
}
base.OnEnable();
UpdateRendererMaterial();
}
/// <summary>
@@ -343,9 +386,15 @@ namespace Coffee.UIExtensions
_isScaleStored = false;
UIParticleUpdater.Unregister(this);
_renderers.ForEach(r => r.Reset());
UnregisterDirtyMaterialCallback(UpdateRendererMaterial);
_canvas = null;
}
base.OnDisable();
/// <summary>
/// Called when the state of the parent Canvas is changed.
/// </summary>
protected override void OnCanvasHierarchyChanged()
{
_canvas = null;
}
/// <summary>
@@ -360,6 +409,7 @@ namespace Coffee.UIExtensions
/// </summary>
protected override void OnTransformParentChanged()
{
_canvas = null;
}
void ISerializationCallbackReceiver.OnBeforeSerialize()
@@ -583,12 +633,14 @@ namespace Coffee.UIExtensions
{
var ps = particleSystems[i];
if (!ps) continue;
GetRenderer(j++).Set(this, ps, false);
var mainEmitter = ps.GetMainEmitter(particleSystems);
GetRenderer(j++).Set(this, ps, false, mainEmitter);
// If the trail is enabled, set it additionally.
if (ps.trails.enabled)
{
GetRenderer(j++).Set(this, ps, true);
GetRenderer(j++).Set(this, ps, true, mainEmitter);
}
}
}
@@ -654,17 +706,6 @@ namespace Coffee.UIExtensions
: Random.Range(m_GroupId, m_GroupMaxId + 1);
}
protected override void UpdateMaterial()
{
}
/// <summary>
/// Call to update the geometry of the Graphic onto the CanvasRenderer.
/// </summary>
protected override void UpdateGeometry()
{
}
private void UpdateRendererMaterial()
{
for (var i = 0; i < _renderers.Count; i++)
@@ -740,7 +781,7 @@ namespace Coffee.UIExtensions
_bakeCamera.useOcclusionCulling = false;
_bakeCamera.gameObject.SetActive(false);
_bakeCamera.gameObject.hideFlags = HideFlags.HideAndDontSave;
_bakeCamera.gameObject.hideFlags = UIParticleProjectSettings.globalHideFlags;
return _bakeCamera;
}

View File

@@ -5,7 +5,7 @@ MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 5f0675613942149309588d556e33d990, type: 3}
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using Coffee.UIParticleExtensions;
using Coffee.UIParticleInternal;
using UnityEngine;
using UnityEngine.Events;
@@ -148,7 +148,7 @@ namespace Coffee.UIExtensions
// Collect UIParticle if needed (same size as m_ParticleSystems)
CollectUIParticlesIfNeeded();
for (var particleIndex = 0; particleIndex < this.m_ParticleSystems.Count; particleIndex++)
for (var particleIndex = 0; particleIndex < m_ParticleSystems.Count; particleIndex++)
{
var particleSystem = m_ParticleSystems[particleIndex];
@@ -163,21 +163,21 @@ namespace Coffee.UIExtensions
particleSystem.GetParticles(particles, count);
var uiParticle = _uiParticles[particleIndex];
var dstPos = this.GetDestinationPosition(uiParticle, particleSystem);
var dstPos = GetDestinationPosition(uiParticle, particleSystem);
for (var i = 0; i < count; i++)
{
// Attracted
var p = particles[i];
if (0f < p.remainingLifetime && Vector3.Distance(p.position, dstPos) < this.m_DestinationRadius)
if (0f < p.remainingLifetime && Vector3.Distance(p.position, dstPos) < m_DestinationRadius)
{
p.remainingLifetime = 0f;
particles[i] = p;
if (this.m_OnAttracted != null)
if (m_OnAttracted != null)
{
try
{
this.m_OnAttracted.Invoke();
m_OnAttracted.Invoke();
}
catch (Exception e)
{
@@ -189,7 +189,7 @@ namespace Coffee.UIExtensions
}
// Calc attracting time
var delayTime = p.startLifetime * this.m_DelayRate;
var delayTime = p.startLifetime * m_DelayRate;
var duration = p.startLifetime - delayTime;
var time = Mathf.Max(0, p.startLifetime - p.remainingLifetime - delayTime);
@@ -197,7 +197,7 @@ namespace Coffee.UIExtensions
if (time <= 0) continue;
// Attract
p.position = this.GetAttractedPosition(p.position, dstPos, duration, time);
p.position = GetAttractedPosition(p.position, dstPos, duration, time);
p.velocity *= 0.5f;
particles[i] = p;
}

View File

@@ -5,7 +5,7 @@ MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
icon: {fileID: 2800000, guid: 5f0675613942149309588d556e33d990, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
#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;
}
[Header("Editor")]
[Tooltip("Hide the automatically generated objects.\n" +
" - UIParticleRenderer\n" +
" - UIParticle BakingCamera")]
[SerializeField]
private bool m_HideGeneratedObjects = true;
public static HideFlags globalHideFlags => instance.m_HideGeneratedObjects
? HideFlags.DontSave | HideFlags.NotEditable | HideFlags.HideInHierarchy | HideFlags.HideInInspector
: HideFlags.DontSave | HideFlags.NotEditable;
#if UNITY_EDITOR
[SettingsProvider]
private static SettingsProvider CreateSettingsProvider()
{
return new PreloadedProjectSettingsProvider("Project/UI/UI Particle");
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f22a23b9d98e440478697f4adf30e61c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 5f0675613942149309588d556e33d990, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -6,7 +6,7 @@
#endif
using System;
using System.Collections.Generic;
using Coffee.UIParticleExtensions;
using Coffee.UIParticleInternal;
using UnityEditor;
using UnityEngine;
using UnityEngine.Profiling;
@@ -15,25 +15,23 @@ using UnityEngine.UI;
namespace Coffee.UIExtensions
{
[Icon("Packages/com.coffee.ui-particle/Icons/UIParticleIcon.png")]
[ExecuteAlways]
[RequireComponent(typeof(RectTransform))]
[RequireComponent(typeof(CanvasRenderer))]
[AddComponentMenu("")]
internal class UIParticleRenderer : MaskableGraphic
{
private static readonly List<Component> s_Components = new List<Component>();
private static readonly CombineInstance[] s_CombineInstances = { new CombineInstance() };
private static readonly List<Material> s_Materials = new List<Material>(2);
private static MaterialPropertyBlock s_Mpb;
private static readonly List<UIParticleRenderer> s_Renderers = new List<UIParticleRenderer>();
private static readonly List<Color32> s_Colors = new List<Color32>();
private static readonly Vector3[] s_Corners = new Vector3[4];
private Material _currentMaterialForRendering;
private bool _delay;
private int _index;
private bool _isPrevStored;
private bool _isTrail;
private Bounds _lastBounds;
private Material _materialForRendering;
private Material _modifiedMaterial;
private UIParticle _parent;
private ParticleSystem _particleSystem;
@@ -43,6 +41,7 @@ namespace Coffee.UIExtensions
private Vector2Int _prevScreenSize;
private bool _preWarm;
private ParticleSystemRenderer _renderer;
private ParticleSystem _mainEmitter;
public override Texture mainTexture => _isTrail ? null : _particleSystem.GetTextureForSprite();
@@ -92,6 +91,19 @@ namespace Coffee.UIExtensions
}
}
public override Material materialForRendering
{
get
{
if (!_materialForRendering)
{
_materialForRendering = base.materialForRendering;
}
return _materialForRendering;
}
}
public void Reset(int index = -1)
{
if (_renderer)
@@ -102,6 +114,7 @@ namespace Coffee.UIExtensions
_parent = null;
_particleSystem = null;
_renderer = null;
_mainEmitter = null;
if (0 <= index)
{
_index = index;
@@ -117,9 +130,8 @@ namespace Coffee.UIExtensions
}
else
{
ModifiedMaterial.Remove(_modifiedMaterial);
_modifiedMaterial = null;
_currentMaterialForRendering = null;
MaterialRepository.Release(ref _modifiedMaterial);
_materialForRendering = null;
}
}
@@ -127,6 +139,7 @@ namespace Coffee.UIExtensions
{
base.OnEnable();
hideFlags = UIParticleProjectSettings.globalHideFlags;
if (!s_CombineInstances[0].mesh)
{
s_CombineInstances[0].mesh = new Mesh
@@ -135,17 +148,14 @@ namespace Coffee.UIExtensions
hideFlags = HideFlags.HideAndDontSave
};
}
_currentMaterialForRendering = null;
}
protected override void OnDisable()
{
base.OnDisable();
ModifiedMaterial.Remove(_modifiedMaterial);
_modifiedMaterial = null;
_currentMaterialForRendering = null;
MaterialRepository.Release(ref _modifiedMaterial);
_materialForRendering = null;
_isPrevStored = false;
}
@@ -154,7 +164,7 @@ namespace Coffee.UIExtensions
// Create renderer object.
var go = new GameObject("[generated] UIParticleRenderer", typeof(UIParticleRenderer))
{
hideFlags = HideFlags.HideAndDontSave,
hideFlags = UIParticleProjectSettings.globalHideFlags,
layer = parent.gameObject.layer
};
@@ -178,12 +188,9 @@ namespace Coffee.UIExtensions
/// </summary>
public override Material GetModifiedMaterial(Material baseMaterial)
{
_currentMaterialForRendering = null;
if (!IsActive() || !_parent)
{
ModifiedMaterial.Remove(_modifiedMaterial);
_modifiedMaterial = null;
MaterialRepository.Release(ref _modifiedMaterial);
return baseMaterial;
}
@@ -193,26 +200,33 @@ namespace Coffee.UIExtensions
var texture = mainTexture;
if (texture == null && _parent.m_AnimatableProperties.Length == 0)
{
ModifiedMaterial.Remove(_modifiedMaterial);
_modifiedMaterial = null;
MaterialRepository.Release(ref _modifiedMaterial);
return modifiedMaterial;
}
//
var id = _parent.m_AnimatableProperties.Length == 0 ? 0 : GetInstanceID();
var hash = new Hash128(
modifiedMaterial ? (uint)modifiedMaterial.GetInstanceID() : 0,
texture ? (uint)texture.GetInstanceID() : 0,
0 < _parent.m_AnimatableProperties.Length ? (uint)GetInstanceID() : 0,
#if UNITY_EDITOR
var props = EditorJsonUtility.ToJson(modifiedMaterial).GetHashCode();
(uint)EditorJsonUtility.ToJson(modifiedMaterial).GetHashCode()
#else
var props = 0;
0
#endif
modifiedMaterial = ModifiedMaterial.Add(modifiedMaterial, texture, id, props);
ModifiedMaterial.Remove(_modifiedMaterial);
_modifiedMaterial = modifiedMaterial;
);
if (!MaterialRepository.Valid(hash, _modifiedMaterial))
{
MaterialRepository.Get(hash, ref _modifiedMaterial, x => new Material(x.mat)
{
hideFlags = HideFlags.HideAndDontSave,
mainTexture = x.texture ? x.texture : x.mat.mainTexture
}, (mat: modifiedMaterial, texture));
}
return modifiedMaterial;
return _modifiedMaterial;
}
public void Set(UIParticle parent, ParticleSystem ps, bool isTrail)
public void Set(UIParticle parent, ParticleSystem ps, bool isTrail, ParticleSystem mainEmitter)
{
_parent = parent;
maskable = parent.maskable;
@@ -235,10 +249,7 @@ namespace Coffee.UIExtensions
ps.TryGetComponent(out _renderer);
_renderer.enabled = false;
//_emitter = emitter;
_isTrail = isTrail;
_renderer.GetSharedMaterials(s_Materials);
material = s_Materials[isTrail ? 1 : 0];
s_Materials.Clear();
@@ -255,6 +266,7 @@ namespace Coffee.UIExtensions
_prevScreenSize = new Vector2Int(Screen.width, Screen.height);
_prevCanvasScale = canvas ? canvas.scaleFactor : 1f;
_delay = true;
_mainEmitter = mainEmitter;
canvasRenderer.SetTexture(null);
@@ -292,7 +304,7 @@ namespace Coffee.UIExtensions
// Simulate particles.
Profiler.BeginSample("[UIParticle] Bake Mesh > Simulate Particles");
if (!_isTrail && _parent.canSimulate)
if (!_isTrail && _parent.canSimulate && !_mainEmitter)
{
#if UNITY_EDITOR
if (!Application.isPlaying)
@@ -405,95 +417,67 @@ namespace Coffee.UIExtensions
_lastBounds = bounds;
// Convert linear color to gamma color.
if (QualitySettings.activeColorSpace == ColorSpace.Linear)
if (UIParticleProjectSettings.enableLinearToGamma && canvas.ShouldGammaToLinearInMesh())
{
Profiler.BeginSample("[UIParticleRenderer] Convert Linear to Gamma");
workerMesh.GetColors(s_Colors);
var count_c = s_Colors.Count;
for (var i = 0; i < count_c; i++)
{
var c = s_Colors[i];
c.r = c.r.LinearToGamma();
c.g = c.g.LinearToGamma();
c.b = c.b.LinearToGamma();
s_Colors[i] = c;
}
workerMesh.SetColors(s_Colors);
Profiler.EndSample();
workerMesh.LinearToGamma();
}
GetComponents(typeof(IMeshModifier), s_Components);
for (var i = 0; i < s_Components.Count; i++)
var components = InternalListPool<Component>.Rent();
GetComponents(typeof(IMeshModifier), components);
for (var i = 0; i < components.Count; i++)
{
#pragma warning disable CS0618 // Type or member is obsolete
((IMeshModifier)s_Components[i]).ModifyMesh(workerMesh);
((IMeshModifier)components[i]).ModifyMesh(workerMesh);
#pragma warning restore CS0618 // Type or member is obsolete
}
s_Components.Clear();
InternalListPool<Component>.Return(ref components);
}
Profiler.EndSample();
// Get grouped renderers.
s_Renderers.Clear();
if (_parent.useMeshSharing)
{
UIParticleUpdater.GetGroupedRenderers(_parent.groupId, _index, s_Renderers);
}
// Set mesh to the CanvasRenderer.
Profiler.BeginSample("[UIParticleRenderer] Set Mesh");
for (var i = 0; i < s_Renderers.Count; i++)
{
if (s_Renderers[i] == this) continue;
s_Renderers[i].canvasRenderer.SetMesh(workerMesh);
s_Renderers[i]._lastBounds = _lastBounds;
}
if (!_parent.canRender)
{
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();
Profiler.EndSample();
// Get grouped renderers.
Profiler.BeginSample("[UIParticleRenderer] Set Mesh");
var renderers = InternalListPool<UIParticleRenderer>.Rent();
if (_parent.useMeshSharing)
{
if (!_currentMaterialForRendering)
{
_currentMaterialForRendering = materialForRendering;
}
UIParticleUpdater.GetGroupedRenderers(_parent.groupId, _index, renderers);
}
for (var i = 0; i < s_Renderers.Count; i++)
{
if (s_Renderers[i] == this) continue;
for (var i = 0; i < renderers.Count; i++)
{
var r = renderers[i];
if (r == this) continue;
s_Renderers[i].canvasRenderer.materialCount = 1;
s_Renderers[i].canvasRenderer.SetMaterial(_currentMaterialForRendering, 0);
}
r.canvasRenderer.SetMesh(workerMesh);
r._lastBounds = _lastBounds;
r.canvasRenderer.materialCount = 1;
r.canvasRenderer.SetMaterial(materialForRendering, 0);
}
InternalListPool<UIParticleRenderer>.Return(ref renderers);
if (_parent.canRender)
{
canvasRenderer.SetMesh(workerMesh);
}
else
{
workerMesh.Clear();
}
Profiler.EndSample();
}
s_Renderers.Clear();
public override void SetMaterialDirty()
{
_materialForRendering = null;
base.SetMaterialDirty();
}
/// <summary>
@@ -559,6 +543,30 @@ namespace Coffee.UIExtensions
return Matrix4x4.Translate(psPos)
* Matrix4x4.Scale(scale);
case ParticleSystemSimulationSpace.World:
if (_isTrail)
{
return Matrix4x4.Translate(psPos)
* Matrix4x4.Scale(scale)
* Matrix4x4.Translate(-psPos);
}
if (_mainEmitter)
{
if (_mainEmitter.IsLocalSpace())
{
return Matrix4x4.Translate(psPos)
* Matrix4x4.Scale(scale)
* Matrix4x4.Translate(-psPos);
}
else
{
psPos = _particleSystem.transform.position - _mainEmitter.transform.position;
return Matrix4x4.Translate(psPos)
* Matrix4x4.Scale(scale)
* Matrix4x4.Translate(-psPos);
}
}
return Matrix4x4.Scale(scale);
case ParticleSystemSimulationSpace.Custom:
return Matrix4x4.Translate(_particleSystem.main.customSimulationSpace.position.GetScaled(scale))
@@ -621,6 +629,7 @@ namespace Coffee.UIExtensions
: main.useUnscaledTime
? Time.unscaledDeltaTime
: Time.deltaTime;
deltaTime *= _parent.timeScaleMultiplier;
// Pre-warm:
if (0 < deltaTime && _preWarm)
@@ -701,12 +710,12 @@ namespace Coffee.UIExtensions
if (s_Mpb.isEmpty) return;
// #41: Copy the value from MaterialPropertyBlock to CanvasRenderer
if (!_modifiedMaterial) return;
if (!materialForRendering) return;
for (var i = 0; i < _parent.m_AnimatableProperties.Length; i++)
{
var ap = _parent.m_AnimatableProperties[i];
ap.UpdateMaterialProperties(_modifiedMaterial, s_Mpb);
ap.UpdateMaterialProperties(materialForRendering, s_Mpb);
}
s_Mpb.Clear();

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using Coffee.UIParticleInternal;
using UnityEditor;
using UnityEngine;
@@ -39,14 +40,26 @@ namespace Coffee.UIExtensions
#if UNITY_EDITOR
[InitializeOnLoadMethod]
#else
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
#endif
private static void InitializeOnLoad()
{
Canvas.willRenderCanvases -= Refresh;
Canvas.willRenderCanvases += Refresh;
UIExtraCallbacks.onAfterCanvasRebuild += Refresh;
EditorApplication.playModeStateChanged += state =>
{
UIExtraCallbacks.onAfterCanvasRebuild -= Refresh;
if (state == PlayModeStateChange.EnteredEditMode || state == PlayModeStateChange.EnteredPlayMode)
{
UIExtraCallbacks.onAfterCanvasRebuild += Refresh;
}
};
}
#else
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void InitializeOnLoad()
{
UIExtraCallbacks.onAfterCanvasRebuild += Refresh;
}
#endif
private static void Refresh()
{

View File

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

View File

@@ -1,96 +1,11 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Coffee.UIParticleExtensions
namespace Coffee.UIParticleInternal
{
public static class Color32Extensions
{
private static byte[] s_LinearToGammaLut;
public static byte LinearToGamma(this byte self)
{
if (s_LinearToGammaLut == null)
{
s_LinearToGammaLut = new byte[256];
for (var i = 0; i < 256; i++)
{
s_LinearToGammaLut[i] = (byte)(Mathf.LinearToGammaSpace(i / 255f) * 255f);
}
}
return s_LinearToGammaLut[self];
}
}
public static class Vector3Extensions
{
public static Vector3 Inverse(this Vector3 self)
{
self.x = Mathf.Approximately(self.x, 0) ? 1 : 1 / self.x;
self.y = Mathf.Approximately(self.y, 0) ? 1 : 1 / self.y;
self.z = Mathf.Approximately(self.z, 0) ? 1 : 1 / self.z;
return self;
}
public static Vector3 GetScaled(this Vector3 self, Vector3 other1)
{
self.Scale(other1);
return self;
}
public static Vector3 GetScaled(this Vector3 self, Vector3 other1, Vector3 other2)
{
self.Scale(other1);
self.Scale(other2);
return self;
}
public static Vector3 GetScaled(this Vector3 self, Vector3 other1, Vector3 other2, Vector3 other3)
{
self.Scale(other1);
self.Scale(other2);
self.Scale(other3);
return self;
}
public static bool IsVisible(this Vector3 self)
{
return 0 < Mathf.Abs(self.x * self.y * self.z);
}
}
internal static class SpriteExtensions
{
#if UNITY_EDITOR
private static readonly Type s_SpriteEditorExtensionType =
Type.GetType("UnityEditor.Experimental.U2D.SpriteEditorExtension, UnityEditor")
?? Type.GetType("UnityEditor.U2D.SpriteEditorExtension, UnityEditor");
private static readonly MethodInfo s_GetActiveAtlasTextureMethodInfo = s_SpriteEditorExtensionType
.GetMethod("GetActiveAtlasTexture", BindingFlags.Static | BindingFlags.NonPublic);
public static Texture2D GetActualTexture(this Sprite self)
{
if (!self) return null;
if (Application.isPlaying) return self.texture;
var ret = s_GetActiveAtlasTextureMethodInfo.Invoke(null, new object[] { self }) as Texture2D;
return ret
? ret
: self.texture;
}
#else
internal static Texture2D GetActualTexture(this Sprite self)
{
return self ? self.texture : null;
}
#endif
}
public static class ParticleSystemExtensions
internal static class ParticleSystemExtensions
{
private static ParticleSystem.Particle[] s_TmpParticles = new ParticleSystem.Particle[2048];
@@ -250,59 +165,38 @@ namespace Coffee.UIParticleExtensions
public static void Exec(this List<ParticleSystem> self, Action<ParticleSystem> action)
{
self.RemoveAll(p => !p);
self.ForEach(action);
}
}
internal static class Misc
{
public static void Destroy(Object obj)
{
if (!obj) return;
#if UNITY_EDITOR
if (!Application.isPlaying)
foreach (var p in self)
{
Object.DestroyImmediate(obj);
}
else
#endif
{
Object.Destroy(obj);
if (!p) continue;
action.Invoke(p);
}
}
public static void DestroyImmediate(Object obj)
public static ParticleSystem GetMainEmitter(this ParticleSystem self, List<ParticleSystem> list)
{
if (!obj) return;
#if UNITY_EDITOR
if (Application.isEditor)
{
Object.DestroyImmediate(obj);
}
else
#endif
{
Object.Destroy(obj);
}
}
if (!self || list == null || list.Count == 0) return null;
#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)
for (var i = 0; i < list.Count; i++)
{
var component = current.GetComponent<T>();
if (component) return component;
current = current.parent;
var parent = list[i];
if (parent != self && IsSubEmitterOf(self, parent)) return parent;
}
return null;
}
#endif
public static bool IsSubEmitterOf(this ParticleSystem self, ParticleSystem parent)
{
if (!self || !parent) return false;
var subEmitters = parent.subEmitters;
var count = subEmitters.subEmittersCount;
for (var i = 0; i < count; i++)
{
if (subEmitters.GetSubEmitterSystem(i) == self) return true;
}
return false;
}
}
}

View File

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

View File

@@ -1,3 +1,4 @@
using Coffee.UIParticleInternal;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
@@ -51,11 +52,7 @@ namespace Coffee.UIExtensions.Demo
public void EnableAnimations(bool flag)
{
#if UNITY_2023_1_OR_NEWER
foreach (var animator in FindObjectsByType<Animator>(FindObjectsInactive.Include, FindObjectsSortMode.None))
#else
foreach (var animator in FindObjectsOfType<Animator>())
#endif
foreach (var animator in Misc.FindObjectsOfType<Animator>())
{
animator.enabled = flag;
}
@@ -83,11 +80,7 @@ namespace Coffee.UIExtensions.Demo
public void UIParticle_Scale(float scale)
{
#if UNITY_2023_1_OR_NEWER
foreach (var uip in FindObjectsByType<UIParticle>(FindObjectsInactive.Include, FindObjectsSortMode.None))
#else
foreach (var uip in FindObjectsOfType<UIParticle>())
#endif
foreach (var uip in Misc.FindObjectsOfType<UIParticle>())
{
uip.scale = scale;
}
@@ -95,11 +88,7 @@ namespace Coffee.UIExtensions.Demo
public void ParticleSystem_WorldSpaseSimulation(bool flag)
{
#if UNITY_2023_1_OR_NEWER
foreach (var p in FindObjectsByType<ParticleSystem>(FindObjectsInactive.Include, FindObjectsSortMode.None))
#else
foreach (var p in FindObjectsOfType<ParticleSystem>())
#endif
foreach (var p in Misc.FindObjectsOfType<ParticleSystem>())
{
var main = p.main;
main.simulationSpace = flag
@@ -135,11 +124,7 @@ namespace Coffee.UIExtensions.Demo
public void ParticleSystem_SetScale(float scale)
{
#if UNITY_2023_1_OR_NEWER
foreach (var ps in FindObjectsByType<ParticleSystem>(FindObjectsInactive.Include, FindObjectsSortMode.None))
#else
foreach (var ps in FindObjectsOfType<ParticleSystem>())
#endif
foreach (var ps in Misc.FindObjectsOfType<ParticleSystem>())
{
ps.transform.localScale = new Vector3(scale, scale, scale);
}

View File

@@ -40,7 +40,10 @@
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Fog { Mode Off }
Fog
{
Mode Off
}
Blend One One
ColorMask [_ColorMask]
@@ -61,21 +64,21 @@
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
@@ -114,4 +117,4 @@
ENDCG
}
}
}
}

View File

@@ -2,8 +2,8 @@
"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.9.0",
"unity": "2018.2",
"version": "5.0.0-preview.16",
"unity": "2019.3",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -0,0 +1,11 @@
{
"MonoBehaviour": {
"m_Enabled": true,
"m_EditorHideFlags": 0,
"m_Name": "",
"m_EditorClassIdentifier": "",
"m_OutputDllPaths": [
"Packages/com.coffee.ui-particle/Runtime/Coffee.UIParticle.R.dll"
]
}
}

View File

@@ -124,7 +124,7 @@ PlayerSettings:
bundleVersion: 1.0
preloadedAssets:
- {fileID: 11400000, guid: 86087a0847f384b538391745dad4565c, type: 2}
- {fileID: 11400000, guid: be3e05903ef7041d39b3ef8ecdd47f08, type: 2}
- {fileID: 11400000, guid: 4b9df7b8a4193489299b8f477348ae0c, type: 2}
metroInputSource: 0
wsaTransparentSwapchain: 0
m_HolographicPauseOnTrackingLoss: 1