Compare commits

..

No commits in common. "3c11209f17d38f154e020cee8b0518be9ee0fb86" and "a499f0c04670e6e52893799f02a30aa7b5b4db05" have entirely different histories.

46 changed files with 1811 additions and 2145 deletions

View File

@ -10,39 +10,26 @@ assignees: mob-sakai
NOTE: Your issue may already be reported! Please search on the [issue tracker](../) before creating one. NOTE: Your issue may already be reported! Please search on the [issue tracker](../) before creating one.
**Describe the bug** **Describe the bug**
A clear and concise description of what the bug is. A clear and concise description of what the bug is.
**To Reproduce** **To Reproduce**
Steps to reproduce the behavior: Steps to reproduce the behavior:
1. Go to '...' 1. Go to '...'
2. Click on '....' 2. Click on '....'
3. Scroll down to '....' 3. Scroll down to '....'
4. See error 4. See error
**Expected behavior** **Expected behavior**
A clear and concise description of what you expected to happen. A clear and concise description of what you expected to happen.
**Screenshots** **Screenshots**
If applicable, add screenshots to help explain your problem. If applicable, add screenshots to help explain your problem.
**Environment (please complete the following)** **Environment (please complete the following information):**
- Version [e.g. 1.0.0]
- Package version [e.g. 4.2.0, 3.3.14] - Platform: [e.g. Editor(Windows/Mac), Standalone(Windows/Mac), iOS, Android, WebGL]
- Platform: [e.g. Editor(Windows/Mac), Standalone(Windows/Mac), iOS, Android, WebGL] - Unity version: [e.g. 2018.2.8f1]
- Unity version: [e.g. 2021.3.7f1] - Build options: [e.g. IL2CPP, .Net 4.x, LWRP]
- Project options: [e.g. Mono/IL2CPP, .Net Standard 2.1/.Net 4.x, URP/HDRP, GraphicAPIs]
**Additional context** **Additional context**
Add any other context about the problem here.
Please add any other context about the issue here.
It will help us resolve the issue.
- Error messages and crash dump
- Screenshots, gif animations and movie files
- Reproducible minimal project
- The entire project (zipped Assets, Packages, ProjectSettings) is preferred over `.unitypackage`

View File

@ -10,17 +10,13 @@ assignees: mob-sakai
NOTE: Your issue may already be reported! Please search on the [issue tracker](../) before creating one. 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.** **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 [...]
A clear and concise description of what the issue is. (e.g. I'm always frustrated when [...])
**Describe the solution you'd like** **Describe the solution you'd like**
A clear and concise description of what you want to happen. A clear and concise description of what you want to happen.
**Describe alternatives you've considered** **Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered. A clear and concise description of any alternative solutions or features you've considered.
**Additional context** **Additional context**
Add any other context or screenshots about the feature request here. Add any other context or screenshots about the feature request here.

View File

@ -13,9 +13,9 @@ jobs:
release: release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- uses: cycjimmy/semantic-release-action@v3 - uses: cycjimmy/semantic-release-action@v2
with: with:
extra_plugins: | extra_plugins: |
@semantic-release/changelog @semantic-release/changelog

View File

@ -36,7 +36,7 @@ jobs:
steps: steps:
# Checkout sandbox project # Checkout sandbox project
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with: with:
ref: sandbox ref: sandbox
submodules: true submodules: true
@ -48,7 +48,7 @@ jobs:
run: git checkout ${{ github.sha }} run: git checkout ${{ github.sha }}
# Cache # Cache
- uses: actions/cache@v3 - uses: actions/cache@v2
with: with:
path: Library path: Library
key: Library-${{ matrix.version }}-${{ github.sha }} key: Library-${{ matrix.version }}-${{ github.sha }}

View File

@ -1,43 +1,3 @@
## [4.2.1](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/4.2.0...4.2.1) (2023-08-18)
### Bug Fixes
* autoScaling and PositionMode may be locked ([3f2f12d](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/3f2f12d2cf7541118c02830ec9fdea8183357487))
# [4.2.0](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/4.1.7...4.2.0) (2023-08-18)
### Bug Fixes
* assertion 'ps->array_size()' in UpdateMesh() when using trails of type ribbon ([f75fcce](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/f75fcce0dae0bc166bd01d36a150aded1fd721f3)), closes [#241](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/241)
* built-in shaders are no longer supported ([c2119c1](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/c2119c171a1262431eac7fea6bf3125db2bcaaca)), closes [#233](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/233) [#257](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/257)
* crash occurs when too many vertices are rendered ([723a04d](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/723a04d0cfd156715a3c92b6d6bd75fdc1862c28))
* error: SerializedObject target has been destroyed ([e930516](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/e93051603e121732c92bcd89ded48087c2b0d7fb)), closes [#267](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/267)
* excessive particle emitted on move ParticleSystem for local space simulation and emission over distance ([2fe0bde](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/2fe0bde422f9769dfedaf6b053ea07f773646679)), closes [#265](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/265)
* fix typos ([52f2ef1](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/52f2ef1f2471a2e1c29fca96255c04b222d9c848))
* generated GameObject will be named '[generated] *' ([9b2e5c1](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/9b2e5c1d1024e091de6d18a4578cd18b43563e48))
* inactive ParticleSystems are removed from the list on refresh ([4851a18](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/4851a1880eef9f385dd9db644ea7e544f95da4fc))
* mesh sharing not working ([8b4ca1a](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/8b4ca1add5c409601e840253e1c0dbcdbf536da8)), closes [#236](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/236)
* nullptr exceptions when using nested UIParticle components in hierarchy ([e67e948](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/e67e9482e2cb840b16e2cfe76e04f7423fcbd3a3)), closes [#246](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/246)
* nullReferenceException after copy-n-paste ([425aad0](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/425aad0cbab475635c72bee84ecbf3f2acedccc2)), closes [#258](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/258)
* remove unnecessary per-frame allocation. ([e92b514](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/e92b514624cc362e53ddeae5ade20fa732f94c7c))
* scaling ParticleSystem puts prewarmed particles in wrong location ([fb31db4](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/fb31db47f2debb3aadbdc4d1b88d0efd9c4ad7bd)), closes [#235](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/235)
* sub-emitters option is not work in editor playing ([b308b26](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/b308b2683372662bb834b6f6d23ea3435a68d1b3)), closes [#231](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/231)
* the camera under UIParticle will be assigned as _orthoCamera ([c42f8c8](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/c42f8c8ab0ff033689349a81e02a4808e071a8a2))
* UIParticleAttractor attracts the particles at wrong position when in RelativeMode ([68d9925](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/68d9925a16237df3c7b07b4781172cbd03425421)), closes [#262](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/262)
### Features
* 'AbsoluteMode' option is renamed to 'PositionMode' ([67eff61](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/67eff610736344ba0122163ff5ee63b25c43f555))
* 'AutoScaling' option will be imported from 'IgnoreCanvasScale' (for v3.x) ([4103041](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/410304125f3f25f3f543c7bc01dcc661eab00609))
* add 'AutoScaling' option for UIParticle ([35325c8](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/35325c88996fa6aea19a6dd1395c05884e1f84ae))
* add 'UpdateMode' option for UIParticleAttractor ([903f702](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/903f702d7be38228841a5a693e3afdceb4a59d9f)), closes [#250](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/250)
* add particle system getter and setter for attractor ([a4bcf93](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/a4bcf93022d2729f3d2a74a2cac4f52e68641b18)), closes [#253](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/253)
* add public properties for UIParticleAttractor ([392ab6d](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/392ab6dd76c36e815320d3a50744d19faa631260)), closes [#253](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/253)
* add Start/StopEmission API for UIParticle ([e499836](https://github.com/mob-sakai/ParticleEffectForUGUI/commit/e4998365c9825fa385e0a317768ce073a1f15b48)), closes [#240](https://github.com/mob-sakai/ParticleEffectForUGUI/issues/240)
## [4.1.7](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/4.1.6...4.1.7) (2022-08-30) ## [4.1.7](https://github.com/mob-sakai/ParticleEffectForUGUI/compare/4.1.6...4.1.7) (2022-08-30)

View File

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

110
README.md
View File

@ -4,12 +4,14 @@ Particle Effect For UGUI (UI Particle)
This plugin provide a component to render particle effect for uGUI in Unity 2018.2 or later. This plugin provide a component to render particle effect for uGUI in Unity 2018.2 or later.
The particle rendering is maskable and sortable, without Camera, RenderTexture or Canvas. The particle rendering is maskable and sortable, without Camera, RenderTexture or Canvas.
![](https://img.shields.io/badge/Editor-2018.2+-4796F2?logo=unity)
[![](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/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?include_prereleases&sort=semver)](https://github.com/mob-sakai/ParticleEffectForUGUI/releases) [![](https://img.shields.io/github/v/release/mob-sakai/ParticleEffectForUGUI?include_prereleases)](https://github.com/mob-sakai/ParticleEffectForUGUI/releases)
[![](https://img.shields.io/github/v/release/mob-sakai/ParticleEffectForUGUI?include_prereleases&sort=semver&filter=3.%2A&label=release(3.x))](https://github.com/mob-sakai/ParticleEffectForUGUI/releases)
[![](https://img.shields.io/github/release-date/mob-sakai/ParticleEffectForUGUI.svg)](https://github.com/mob-sakai/ParticleEffectForUGUI/releases) [![](https://img.shields.io/github/license/mob-sakai/ParticleEffectForUGUI.svg)](https://github.com/mob-sakai/ParticleEffectForUGUI/blob/master/LICENSE.txt) [![](https://img.shields.io/github/release-date/mob-sakai/ParticleEffectForUGUI.svg)](https://github.com/mob-sakai/ParticleEffectForUGUI/releases) [![](https://img.shields.io/github/license/mob-sakai/ParticleEffectForUGUI.svg)](https://github.com/mob-sakai/ParticleEffectForUGUI/blob/master/LICENSE.txt)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-orange.svg)](http://makeapullrequest.com) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-orange.svg)](http://makeapullrequest.com)
![](https://img.shields.io/badge/Unity%202018.2+-supported-blue.svg)
![](https://img.shields.io/badge/Unity%202019.x-supported-blue.svg)
![](https://img.shields.io/badge/Unity%202020.x-supported-blue.svg)
![](https://img.shields.io/badge/Universal%20Rendering%20Pipeline-supported-blue.svg)
<< [Description](#Description) | [Demo](#demo) | [Installation](#installation) | [Usage](#usage) | [Development Note](#development-note) | [Change log](https://github.com/mob-sakai/ParticleEffectForUGUI/blob/upm/CHANGELOG.md) >> << [Description](#Description) | [Demo](#demo) | [Installation](#installation) | [Usage](#usage) | [Development Note](#development-note) | [Change log](https://github.com/mob-sakai/ParticleEffectForUGUI/blob/upm/CHANGELOG.md) >>
@ -56,8 +58,6 @@ Compares this "Baking mesh" approach with the conventional approach:
* Support CanvasGroup alpha * Support CanvasGroup alpha
* No allocations * No allocations
* Support overlay, camera space and world space * Support overlay, camera space and world space
* Support Universal Render Pipeline (URP) and High Definition Render Pipeline (HDRP)
* Support disabling `Enter Play Mode Options > Reload Domain`
* Support changing material property with AnimationClip (AnimatableProperty) * Support changing material property with AnimationClip (AnimatableProperty)
![](https://user-images.githubusercontent.com/12690315/53286323-2d94a980-37b0-11e9-8afb-c4a207805ff2.gif) ![](https://user-images.githubusercontent.com/12690315/53286323-2d94a980-37b0-11e9-8afb-c4a207805ff2.gif)
* [4.0.0+] Support 8+ materials * [4.0.0+] Support 8+ materials
@ -92,41 +92,39 @@ Compares this "Baking mesh" approach with the conventional approach:
### Requirement ### Requirement
Unity 2018.2 or later ![](https://img.shields.io/badge/Unity%202018.2+-supported-blue.svg)
![](https://img.shields.io/badge/Unity%202019.x-supported-blue.svg)
![](https://img.shields.io/badge/Unity%202020.x-supported-blue.svg)
![](https://img.shields.io/badge/Universal%20Rendering%20Pipeline-supported-blue.svg)
### Install via OpenUPM ### Using OpenUPM
This package is available on [OpenUPM](https://openupm.com). This package is available on [OpenUPM](https://openupm.com).
It's recommended to install it via [openupm-cli](https://github.com/openupm/openupm-cli). You can install it via [openupm-cli](https://github.com/openupm/openupm-cli).
``` ```
openupm add com.coffee.ui-particle openupm add com.coffee.ui-particle
``` ```
### Install via Package Manager Window (using Git URL) ### Using Git
1. Select `Window/Package Manager` menu to open `Package Manager` window. Find the manifest.json file in the Packages folder of your project and add a line to `dependencies` field.
2. Click `+` and `Install package from git URL...`
![](https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/57317aa4-e55c-4568-b2aa-2ee2a78ddc02)
3. Input `https://github.com/mob-sakai/ParticleEffectForUGUI.git` and click `Install`
![](https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/72cc38b5-cb32-4c85-8209-c85f7bb931ea)
* `"com.coffee.ui-particle": "https://github.com/mob-sakai/ParticleEffectForUGUI.git"`
To update the package, change suffix `#{version}` to the target version.
* `"com.coffee.ui-particle": "https://github.com/mob-sakai/ParticleEffectForUGUI.git#4.0.0",`
Or, use [UpmGitExtension](https://github.com/mob-sakai/UpmGitExtension) to install and update the package. Or, use [UpmGitExtension](https://github.com/mob-sakai/UpmGitExtension) to install and update the package.
### Install via manifest.json (using Git URL) ### For Unity 2018.2
1. Open `Packages/manifest.json` in your project. Unity 2018.2 supports embedded packages.
2. Add this line below `"dependencies": {`
* `"com.coffee.ui-particle": "https://github.com/mob-sakai/ParticleEffectForUGUI.git",`
![](https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/fe1cad9c-8d09-4e17-b6f5-9eac0d267921)
3. To update the package, change suffix `#{version}`.
* `"com.coffee.ui-particle": "https://github.com/mob-sakai/ParticleEffectForUGUI.git#4.2.0",`
### Install as an embed package
1. Download a source code zip file from [Releases](https://github.com/mob-sakai/ParticleEffectForUGUI/releases) page 1. Download a source code zip file from [Releases](https://github.com/mob-sakai/ParticleEffectForUGUI/releases) page
2. Extract it and place it under `Packages` directory in your project. 2. Extract it
![](https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/4c3d3439-5bb0-4e87-a917-ebe328ef89a8) 3. Import it under `Packages` directory in your Unity project
<br><br><br><br> <br><br><br><br>
@ -163,17 +161,17 @@ Or, use [UpmGitExtension](https://github.com/mob-sakai/UpmGitExtension) to insta
`UIParticle` controls the ParticleSystems that is attached to its own game objects and child game objects. `UIParticle` controls the ParticleSystems that is attached to its own game objects and child game objects.
![](https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/3559df45-63e7-4c4c-9233-f455779efa29) ![](https://user-images.githubusercontent.com/12690315/174413976-691eb38e-7f92-4fbe-9790-8771b9dc70b2.png)
| Properties | Description | | Properties | Description |
| -- | -- | | -- | -- |
| Maskable | Does this graphic allow masking. | | Maskable | Does this graphic allow masking. |
| Ignore Canvas Scale | Ignore the scale of the root canvas.<br>This prevents it from displaying small even in hierarchy scaling mode of `ParticleSystem`. |
| Scale | Scale the rendering.<br>When the `3D` toggle is enabled, 3D scale (x,y,z) is supported. | | Scale | Scale the rendering.<br>When the `3D` toggle is enabled, 3D scale (x,y,z) is supported. |
| Animatable Properties | If you want update material properties (e.g. `_MainTex_ST`, `_Color`) in AnimationClip, use this to mark the changes. | | Animatable Properties | If you want update material properties (e.g. `_MainTex_ST`, `_Color`) in AnimationClip, use this to mark the changes. |
| Mesh Sharing | Particle simulation results are shared within the same group.<br>A large number of the same effects can be displayed with a small load.<br>When the `Random` toggle is enabled, it will be grouped randomaly. | | Mesh Sharing | Particle simulation results are shared within the same group.<br>A large number of the same effects can be displayed with a small load.<br>When the `Random` toggle is enabled, it will be grouped randomaly. |
| Position Mode | **Absolute:** Emit from the world position of the `ParticleSystem`.<br>**Relative:** Emit from the scaled position of the `ParticleSystem`. | | Absolute Mode | The particles will be emitted at the ParticleSystem position.<br>Move the UIParticle/ParticleSystem to move the particle. |
| Auto Scaling | Transform.lossyScale (=world scale) will be set to `(1, 1, 1)` on update.<br>It prevents the root-Canvas scale from affecting the hierarchy-scaled `ParticleSystem`. | | Rendering Order | The ParticleSystems to be rendered.<br>You can change the rendering order and the materials. |
| Rendering Order | The ParticleSystem list to be rendered.<br>You can change the order and the materials. |
NOTE: Press `Refresh` button to reconstruct rendering order based on children ParticleSystem's sorting order and z position. NOTE: Press `Refresh` button to reconstruct rendering order based on children ParticleSystem's sorting order and z position.
@ -229,7 +227,7 @@ uiParticle.Stop();
`UIParticleAttractor` attracts particles generated by the specified ParticleSystem. `UIParticleAttractor` attracts particles generated by the specified ParticleSystem.
![](https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/ea6ae0ed-f9a8-437c-8baa-47526303391e) ![](https://user-images.githubusercontent.com/12690315/174413982-b31c358a-8e1d-4b3e-a6d8-18b050b25d6f.png)
![](https://user-images.githubusercontent.com/12690315/174311027-462929a4-13f0-4ec4-86ea-9c832f2eecf1.gif) ![](https://user-images.githubusercontent.com/12690315/174311027-462929a4-13f0-4ec4-86ea-9c832f2eecf1.gif)
| Properties | Description | | Properties | Description |
@ -239,66 +237,22 @@ uiParticle.Stop();
| Delay Rate | Delay to start attracting.<br>It is a percentage of the particle's start lifetime. | | Delay Rate | Delay to start attracting.<br>It is a percentage of the particle's start lifetime. |
| Max Speed | Maximum speed of attracting.<br> If this value is too small, attracting may not be completed by the end of the lifetime and `OnAttracted` may not be called. | | Max Speed | Maximum speed of attracting.<br> If this value is too small, attracting may not be completed by the end of the lifetime and `OnAttracted` may not be called. |
| Movement | Attracting movement type. (Linear, Smooth, Sphere) | | Movement | Attracting movement type. (Linear, Smooth, Sphere) |
| Update Mode | **Normal:** Update with scaled delta time.<br>**Unscaled Time:** Update with unscaled delta time. |
| OnAttracted | An event called when attracting is complete (per particle). | | OnAttracted | An event called when attracting is complete (per particle). |
<br><br><br><br> <br><br><br><br>
## Development Note ## Development Note
### FAQ: Why are my particle effects not displayed correctly?
If `ParticleSystem` alone displays particles correctly but `UIParticle` does not, please check the following points:
* [Shader Limitation](https://github.com/mob-sakai/ParticleEffectForUGUI#shader-limitation)
* Most cases can be solved by using `UI/Additive` or `UI/Default`.
* Particles are masked
* `UIParticle` is maskable.
* Set `Mask` or `RectMask2D` component properly.
* Particles are too small
* If particles are small enough, they will not appear on the screen.
* Increase the `Scale` value.
* If you don't want to change the apparent size depending on the resolution, try `Auto Scaling` option.
* Particles are too many
* No more than 65535 vertices can be displayed (for mesh combination limitations).
* Please set `Emission` module and `Max Particles` of ParticleSystem properly.
* Particles are emitted off-screen.
* When `Position Mode = Relative`, particles are emitted from scaled position of the ParticleSystem, not from the screen point of the ParticleSystem.
* Place the ParticleSystem in the proper position or try `Position Mode = Absolute`.
* Attaching `UIParticle` to the same object as `ParticleSystem`
* `Transform.localScale` will be overridden by `Auto Scaling` option.
* It is recommended to place `ParticleSystem` under `UIParticle`.
<br>
### Shader Limitation ### Shader Limitation
The use of UI shaders is recommended. UIParticles are based on UIVertex.
Therefore, only xy components is available for each UV in the shader. (zw components will be ignored).
- If you need a simple Additive shader, use the `UI/Additive` shader instead.
- If you need a simple alpha-blend shader, use the `UI/Default` shader instead.
- If your custom shader does not work properly with UIParticle, consider creating a custom UI shader.
#### Built-in shaders are not supported
UIParticle does not support all built-in shaders except for `UI/Default`.
If their use is detected, an error is displayed in the inspector.
Use UI shaders instead.
#### UV.zw components will be discarded
UIParticleRenderer renders the particles based on UIVertex.
Therefore, only xy components is available for each UV in the shader. (zw components will be discarded).
So unfortunately UIParticles will not work well with some shaders. So unfortunately UIParticles will not work well with some shaders.
#### Custom vertex streams
When using custom vertex streams, you can fill zw components with "unnecessary" data. When using custom vertex streams, you can fill zw components with "unnecessary" data.
https://github.com/mob-sakai/ParticleEffectForUGUI/issues/191 https://github.com/mob-sakai/ParticleEffectForUGUI/issues/191
- If you need a simple Additive shader, use the `UI/Additive` shader instead.
<br> - If you need a simple alpha-blend shader, use the `UI/Default` shader instead.
### Overheads ### Overheads

View File

@ -1,51 +1,33 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Coffee.UIExtensions;
using UnityEditor;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
using UnityEngine.UI; using UnityEngine.UI;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
namespace Coffee.UIExtensions.Demo public class CFX_Demo_With_UIParticle : MonoBehaviour
{ {
public class CFX_Demo_With_UIParticle : MonoBehaviour private UIParticle UiParticle;
{ private Toggle spawnOnUI;
private MonoBehaviour _demo; private MonoBehaviour demo;
private Toggle _spawnOnUI;
private UIParticle _uiParticle;
// Start is called before the first frame update // Start is called before the first frame update
private void Start() private void Start()
{ {
_uiParticle = GetComponentInChildren<UIParticle>(); UiParticle = GetComponentInChildren<UIParticle>();
_spawnOnUI = GetComponentInChildren<Toggle>(); spawnOnUI = GetComponentInChildren<Toggle>();
_demo = FindObjectOfType("CFX_Demo_New") as MonoBehaviour
demo = FindObjectOfType("CFX_Demo_New") as MonoBehaviour
?? FindObjectOfType("WFX_Demo_New") as MonoBehaviour; ?? FindObjectOfType("WFX_Demo_New") as MonoBehaviour;
SetCanvasWidth(800); SetCanvasWidth(800);
SetCanvasRenderOverlay(true); SetCanvasRenderOverlay(true);
} }
// Update is called once per frame private Object FindObjectOfType(string typeName)
private void Update()
{
if (!_spawnOnUI.isOn || !_demo || !Input.GetMouseButtonDown(0)) return;
foreach (Transform child in _uiParticle.transform)
{
Destroy(child.gameObject);
}
var particle = _demo.GetType()
.GetMethod("spawnParticle", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
?.Invoke(_demo, Array.Empty<object>()) as GameObject;
if (!particle) return;
particle.transform.localScale = Vector3.one;
_uiParticle.SetParticleSystemInstance(particle);
}
private static Object FindObjectOfType(string typeName)
{ {
var type = AppDomain.CurrentDomain.GetAssemblies() var type = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes()) .SelectMany(x => x.GetTypes())
@ -54,6 +36,23 @@ namespace Coffee.UIExtensions.Demo
return type == null ? null : FindObjectOfType(type); return type == null ? null : FindObjectOfType(type);
} }
// Update is called once per frame
private void Update()
{
if (!spawnOnUI.isOn || !demo || !Input.GetMouseButtonDown(0)) return;
foreach (Transform child in UiParticle.transform)
{
Destroy(child.gameObject);
}
var particle = demo.GetType()
.GetMethod("spawnParticle", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
.Invoke(demo, new object[0]) as GameObject;
particle.transform.localScale = Vector3.one;
UiParticle.SetParticleSystemInstance(particle);
}
public void SetCanvasWidth(int width) public void SetCanvasWidth(int width)
{ {
var scaler = GetComponentInParent<CanvasScaler>(); var scaler = GetComponentInParent<CanvasScaler>();
@ -83,5 +82,4 @@ namespace Coffee.UIExtensions.Demo
{ {
SceneManager.LoadScene(scene); SceneManager.LoadScene(scene);
} }
}
} }

View File

@ -1,28 +1,22 @@
using UnityEngine; using UnityEngine;
using UnityEngine.Serialization;
namespace Coffee.UIExtensions.Demo namespace Coffee.UIExtensions.Demo
{ {
public class CopyItemOnStart : MonoBehaviour public class CopyItemOnStart : MonoBehaviour
{ {
[FormerlySerializedAs("origin")] public GameObject origin;
[SerializeField] public int count;
private GameObject m_Origin;
[FormerlySerializedAs("count")]
[SerializeField]
private int m_Count;
private void Start() private void Start()
{ {
if (!m_Origin) return; if (!origin) return;
m_Origin.SetActive(false); origin.SetActive(false);
var parent = m_Origin.transform.parent; var parent = origin.transform.parent;
for (var i = 0; i < m_Count; i++) for (var i = 0; i < count; i++)
{ {
var go = Instantiate(m_Origin, parent, false); var go = Instantiate(origin, parent, false);
go.name = $"{m_Origin.name} {i + 1}"; go.name = string.Format("{0} {1}", origin.name, i + 1);
go.hideFlags = HideFlags.DontSave; go.hideFlags = HideFlags.DontSave;
go.SetActive(true); go.SetActive(true);
} }

View File

@ -1,31 +1,25 @@
using System; using UnityEngine;
using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityEngine.Serialization;
public class UIElementDragger : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler public class UIElementDragger : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{ {
[SerializeField] public enum Target
private Target m_Target; {
Self,
Parent,
Custom,
}
[SerializeField] private RectTransform rectTransform;
private Transform m_CustomTarget; private Canvas canvas;
public Target m_Target;
[FormerlySerializedAs("ex2")] public Transform m_CustomTarget;
[SerializeField] public bool ex2;
private bool m_UseCanvasScale;
private Canvas _canvas;
private RectTransform _rectTransform;
private void OnEnable() private void OnEnable()
{ {
_rectTransform = GetComponent<RectTransform>(); rectTransform = GetComponent<RectTransform>();
_canvas = GetComponentInParent<Canvas>(); canvas = GetComponentInParent<Canvas>();
}
public void OnBeginDrag(PointerEventData eventData)
{
} }
public void OnDrag(PointerEventData eventData) public void OnDrag(PointerEventData eventData)
@ -35,34 +29,34 @@ public class UIElementDragger : MonoBehaviour, IBeginDragHandler, IDragHandler,
switch (m_Target) switch (m_Target)
{ {
case Target.Self: case Target.Self:
_rectTransform.localPosition += delta; rectTransform.localPosition += delta;
break; break;
case Target.Parent: case Target.Parent:
_rectTransform.parent.localPosition += delta; rectTransform.parent.localPosition += delta;
break; break;
case Target.Custom: case Target.Custom:
_rectTransform.localPosition += delta; rectTransform.localPosition += delta;
if (m_CustomTarget) if (m_CustomTarget)
{ {
if (m_UseCanvasScale) if (ex2)
{ delta.Scale(canvas.rootCanvas.transform.localScale);
delta.Scale(_canvas.rootCanvas.transform.localScale);
}
m_CustomTarget.localPosition += delta; m_CustomTarget.localPosition += delta;
} }
break; break;
} }
} }
public void OnBeginDrag(PointerEventData eventData)
{
}
public void OnEndDrag(PointerEventData eventData) public void OnEndDrag(PointerEventData eventData)
{ {
} }
private Vector2 GetLocalDelta(Vector2 evDelta) private Vector2 GetLocalDelta(Vector2 evDelta)
{ {
switch (_canvas.renderMode) switch (canvas.renderMode)
{ {
case RenderMode.ScreenSpaceOverlay: case RenderMode.ScreenSpaceOverlay:
{ {
@ -71,23 +65,21 @@ public class UIElementDragger : MonoBehaviour, IBeginDragHandler, IDragHandler,
return delta - zero; return delta - zero;
} }
case RenderMode.ScreenSpaceCamera: case RenderMode.ScreenSpaceCamera:
{
Vector2 zero, delta;
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, Vector2.zero, canvas.worldCamera, out zero);
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, evDelta, canvas.worldCamera, out delta);
return delta - zero;
}
case RenderMode.WorldSpace: case RenderMode.WorldSpace:
{ {
RectTransformUtility.ScreenPointToLocalPointInRectangle(_rectTransform, Vector2.zero, Vector3 zero, delta;
_canvas.worldCamera, out var zero); RectTransformUtility.ScreenPointToWorldPointInRectangle(rectTransform, Vector2.zero, canvas.worldCamera, out zero);
RectTransformUtility.ScreenPointToLocalPointInRectangle(_rectTransform, evDelta, RectTransformUtility.ScreenPointToWorldPointInRectangle(rectTransform, evDelta, canvas.worldCamera, out delta);
_canvas.worldCamera, out var delta);
return delta - zero; return delta - zero;
} }
default: default:
throw new NotSupportedException(); throw new System.NotSupportedException();
} }
} }
private enum Target
{
Self,
Parent,
Custom
}
} }

View File

@ -1,18 +1,14 @@
using UnityEngine; using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI; using UnityEngine.UI;
namespace Coffee.UIExtensions.Demo namespace Coffee.UIExtensions.Demo
{ {
public class UIParticle_Demo : MonoBehaviour public class UIParticle_Demo : MonoBehaviour
{ {
[FormerlySerializedAs("root")] public Canvas root;
[SerializeField]
private Canvas m_RootCanvas;
private int _height;
private int _score;
private int _width; private int _width;
private int _height;
private void Start() private void Start()
{ {
@ -28,18 +24,11 @@ namespace Coffee.UIExtensions.Demo
case RuntimePlatform.WindowsPlayer: case RuntimePlatform.WindowsPlayer:
case RuntimePlatform.LinuxPlayer: case RuntimePlatform.LinuxPlayer:
if (Screen.width == _width && Screen.height == _height) if (Screen.width == _width && Screen.height == _height)
{
Screen.SetResolution(_height, _width, Screen.fullScreen); Screen.SetResolution(_height, _width, Screen.fullScreen);
}
else if (Screen.width == _height && Screen.height == _width) else if (Screen.width == _height && Screen.height == _width)
{
Screen.SetResolution(Mathf.Min(_width, _height), Mathf.Min(_width, _height), Screen.fullScreen); Screen.SetResolution(Mathf.Min(_width, _height), Mathf.Min(_width, _height), Screen.fullScreen);
}
else else
{
Screen.SetResolution(_width, _height, Screen.fullScreen); Screen.SetResolution(_width, _height, Screen.fullScreen);
}
break; break;
} }
} }
@ -49,31 +38,27 @@ namespace Coffee.UIExtensions.Demo
Screen.fullScreen = !Screen.fullScreen; Screen.fullScreen = !Screen.fullScreen;
} }
public void EnableAnimations(bool flag) public void EnableAnimations(bool enabled)
{ {
foreach (var animator in FindObjectsOfType<Animator>()) foreach (var animator in FindObjectsOfType<Animator>())
{ {
animator.enabled = flag; animator.enabled = enabled;
} }
} }
public void UIParticle_MeshSharing(bool flag) public void UIParticle_MeshSharing(bool enabled)
{ {
foreach (var uip in m_RootCanvas.GetComponentsInChildren<UIParticle>(true)) foreach (var uip in root.GetComponentsInChildren<UIParticle>(true))
{ {
uip.meshSharing = flag uip.meshSharing = enabled ? UIParticle.MeshSharing.Auto : UIParticle.MeshSharing.None;
? UIParticle.MeshSharing.Auto
: UIParticle.MeshSharing.None;
} }
} }
public void UIParticle_RandomGroup(bool flag) public void UIParticle_RandomGroup(bool enabled)
{ {
foreach (var uip in m_RootCanvas.GetComponentsInChildren<UIParticle>(true)) foreach (var uip in root.GetComponentsInChildren<UIParticle>(true))
{ {
uip.groupMaxId = flag uip.groupMaxId = enabled ? 4 : 0;
? 4
: 0;
} }
} }
@ -85,40 +70,38 @@ namespace Coffee.UIExtensions.Demo
} }
} }
public void ParticleSystem_WorldSpaseSimulation(bool flag) public void ParticleSystem_WorldSpaseSimulation(bool enabled)
{ {
foreach (var p in FindObjectsOfType<ParticleSystem>()) foreach (var ps in FindObjectsOfType<ParticleSystem>())
{ {
var main = p.main; var main = ps.main;
main.simulationSpace = flag main.simulationSpace = enabled ? ParticleSystemSimulationSpace.World : ParticleSystemSimulationSpace.Local;
? ParticleSystemSimulationSpace.World
: ParticleSystemSimulationSpace.Local;
} }
} }
public void ParticleSystem_WorldSpaseSimulation(ParticleSystem ps) public void ParticleSystem_WorldSpaseSimulation(ParticleSystem particleSystem)
{ {
foreach (var p in ps.GetComponentsInChildren<ParticleSystem>()) foreach (var ps in particleSystem.GetComponentsInChildren<ParticleSystem>())
{ {
var main = p.main; var main = ps.main;
main.simulationSpace = ParticleSystemSimulationSpace.World; main.simulationSpace = ParticleSystemSimulationSpace.World;
p.Clear(); ps.Clear();
} }
} }
public void ParticleSystem_LocalSpaseSimulation(ParticleSystem ps) public void ParticleSystem_LocalSpaseSimulation(ParticleSystem particleSystem)
{ {
foreach (var p in ps.GetComponentsInChildren<ParticleSystem>()) foreach (var ps in particleSystem.GetComponentsInChildren<ParticleSystem>())
{ {
var main = p.main; var main = ps.main;
main.simulationSpace = ParticleSystemSimulationSpace.Local; main.simulationSpace = ParticleSystemSimulationSpace.Local;
p.Clear(); ps.Clear();
} }
} }
public void ParticleSystem_Emit(ParticleSystem ps) public void ParticleSystem_Emit(ParticleSystem particleSystem)
{ {
ps.Emit(5); particleSystem.Emit(5);
} }
public void ParticleSystem_SetScale(float scale) public void ParticleSystem_SetScale(float scale)
@ -144,37 +127,41 @@ namespace Coffee.UIExtensions.Demo
attractor.movement = UIParticleAttractor.Movement.Sphere; attractor.movement = UIParticleAttractor.Movement.Sphere;
} }
int score = 0;
public void UIParticleAttractor_OnAttract(Text scoreText) public void UIParticleAttractor_OnAttract(Text scoreText)
{ {
_score++; score++;
scoreText.text = _score.ToString(); scoreText.text = score.ToString();
scoreText.GetComponent<Animator>().Play(0); scoreText.GetComponent<Animator>().Play(0);
} }
public void Canvas_WorldSpace(bool flag) public void Canvas_WorldSpace(bool flag)
{ {
if (!flag) return; if (flag)
{
var canvas = FindObjectOfType<Canvas>(); var canvas = FindObjectOfType<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceCamera; canvas.renderMode = RenderMode.ScreenSpaceCamera;
canvas.renderMode = RenderMode.WorldSpace; canvas.renderMode = RenderMode.WorldSpace;
canvas.transform.rotation = Quaternion.Euler(new Vector3(0, 10, 0)); canvas.transform.rotation = Quaternion.Euler(new Vector3(0, 10, 0));
} }
}
public void Canvas_CameraSpace(bool flag) public void Canvas_CameraSpace(bool flag)
{ {
if (!flag) return; if (flag)
{
var canvas = FindObjectOfType<Canvas>(); var canvas = FindObjectOfType<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceCamera; canvas.renderMode = RenderMode.ScreenSpaceCamera;
} }
}
public void Canvas_Overlay(bool flag) public void Canvas_Overlay(bool flag)
{ {
if (!flag) return; if (flag)
{
var canvas = FindObjectOfType<Canvas>(); var canvas = FindObjectOfType<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay; canvas.renderMode = RenderMode.ScreenSpaceOverlay;
} }
} }
}
} }

View File

@ -1,37 +1,30 @@
using UnityEngine; using UnityEngine;
using UnityEngine.Serialization;
namespace Coffee.UIExtensions.Demo namespace Coffee.UIExtensions.Demo
{ {
public class UIParticle_Demo_UIParticleController : MonoBehaviour public class UIParticle_Demo_UIParticleController : MonoBehaviour
{ {
[FormerlySerializedAs("root")] public Transform root;
[SerializeField]
private Transform m_RootTransform;
public void UIParticle_MeshSharing(bool flag) public void UIParticle_MeshSharing(bool enabled)
{ {
foreach (var uip in m_RootTransform.GetComponentsInChildren<UIParticle>(true)) foreach (var uip in root.GetComponentsInChildren<UIParticle>(true))
{ {
uip.meshSharing = flag uip.meshSharing = enabled ? UIParticle.MeshSharing.Auto : UIParticle.MeshSharing.None;
? UIParticle.MeshSharing.Auto
: UIParticle.MeshSharing.None;
} }
} }
public void UIParticle_RandomGroup(bool flag) public void UIParticle_RandomGroup(bool enabled)
{ {
foreach (var uip in m_RootTransform.GetComponentsInChildren<UIParticle>(true)) foreach (var uip in root.GetComponentsInChildren<UIParticle>(true))
{ {
uip.groupMaxId = flag uip.groupMaxId = enabled ? 4 : 0;
? 4
: 0;
} }
} }
public void UIParticle_Scale(float scale) public void UIParticle_Scale(float scale)
{ {
foreach (var uip in m_RootTransform.GetComponentsInChildren<UIParticle>(true)) foreach (var uip in root.GetComponentsInChildren<UIParticle>(true))
{ {
uip.scale = scale; uip.scale = scale;
} }

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: d07e6c2670f164cf7939ab011061a9bf guid: 70069a8db1df148ae8b4c8108c079acf
TrueTypeFontImporter: TrueTypeFontImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 4 serializedVersion: 4
@ -8,9 +8,9 @@ TrueTypeFontImporter:
characterSpacing: 0 characterSpacing: 0
characterPadding: 1 characterPadding: 1
includeFontData: 1 includeFontData: 1
fontName: Share Tech Mono fontName: Falstin
fontNames: fontNames:
- Share Tech Mono - Falstin
fallbackFontReferences: [] fallbackFontReferences: []
customCharacters: customCharacters:
fontRenderingMode: 1 fontRenderingMode: 1

View File

@ -127,7 +127,7 @@ MonoBehaviour:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
m_FontData: m_FontData:
m_Font: {fileID: 12800000, guid: d07e6c2670f164cf7939ab011061a9bf, type: 3} m_Font: {fileID: 12800000, guid: 70069a8db1df148ae8b4c8108c079acf, type: 3}
m_FontSize: 14 m_FontSize: 14
m_FontStyle: 0 m_FontStyle: 0
m_BestFit: 0 m_BestFit: 0
@ -206,7 +206,7 @@ MonoBehaviour:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
m_FontData: m_FontData:
m_Font: {fileID: 12800000, guid: d07e6c2670f164cf7939ab011061a9bf, type: 3} m_Font: {fileID: 12800000, guid: 70069a8db1df148ae8b4c8108c079acf, type: 3}
m_FontSize: 14 m_FontSize: 14
m_FontStyle: 0 m_FontStyle: 0
m_BestFit: 0 m_BestFit: 0
@ -285,7 +285,7 @@ MonoBehaviour:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
m_FontData: m_FontData:
m_Font: {fileID: 12800000, guid: d07e6c2670f164cf7939ab011061a9bf, type: 3} m_Font: {fileID: 12800000, guid: 70069a8db1df148ae8b4c8108c079acf, type: 3}
m_FontSize: 14 m_FontSize: 14
m_FontStyle: 0 m_FontStyle: 0
m_BestFit: 0 m_BestFit: 0
@ -366,7 +366,7 @@ MonoBehaviour:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
m_FontData: m_FontData:
m_Font: {fileID: 12800000, guid: d07e6c2670f164cf7939ab011061a9bf, type: 3} m_Font: {fileID: 12800000, guid: 70069a8db1df148ae8b4c8108c079acf, type: 3}
m_FontSize: 14 m_FontSize: 14
m_FontStyle: 0 m_FontStyle: 0
m_BestFit: 0 m_BestFit: 0
@ -516,7 +516,7 @@ MonoBehaviour:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
m_FontData: m_FontData:
m_Font: {fileID: 12800000, guid: d07e6c2670f164cf7939ab011061a9bf, type: 3} m_Font: {fileID: 12800000, guid: 70069a8db1df148ae8b4c8108c079acf, type: 3}
m_FontSize: 14 m_FontSize: 14
m_FontStyle: 0 m_FontStyle: 0
m_BestFit: 0 m_BestFit: 0
@ -595,7 +595,7 @@ MonoBehaviour:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
m_FontData: m_FontData:
m_Font: {fileID: 12800000, guid: d07e6c2670f164cf7939ab011061a9bf, type: 3} m_Font: {fileID: 12800000, guid: 70069a8db1df148ae8b4c8108c079acf, type: 3}
m_FontSize: 14 m_FontSize: 14
m_FontStyle: 0 m_FontStyle: 0
m_BestFit: 0 m_BestFit: 0
@ -736,7 +736,7 @@ MonoBehaviour:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
m_FontData: m_FontData:
m_Font: {fileID: 12800000, guid: d07e6c2670f164cf7939ab011061a9bf, type: 3} m_Font: {fileID: 12800000, guid: 70069a8db1df148ae8b4c8108c079acf, type: 3}
m_FontSize: 16 m_FontSize: 16
m_FontStyle: 0 m_FontStyle: 0
m_BestFit: 0 m_BestFit: 0
@ -889,7 +889,7 @@ MonoBehaviour:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
m_FontData: m_FontData:
m_Font: {fileID: 12800000, guid: d07e6c2670f164cf7939ab011061a9bf, type: 3} m_Font: {fileID: 12800000, guid: 70069a8db1df148ae8b4c8108c079acf, type: 3}
m_FontSize: 16 m_FontSize: 16
m_FontStyle: 0 m_FontStyle: 0
m_BestFit: 0 m_BestFit: 0
@ -1042,7 +1042,7 @@ MonoBehaviour:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
m_FontData: m_FontData:
m_Font: {fileID: 12800000, guid: d07e6c2670f164cf7939ab011061a9bf, type: 3} m_Font: {fileID: 12800000, guid: 70069a8db1df148ae8b4c8108c079acf, type: 3}
m_FontSize: 14 m_FontSize: 14
m_FontStyle: 0 m_FontStyle: 0
m_BestFit: 0 m_BestFit: 0
@ -1206,7 +1206,7 @@ MonoBehaviour:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
m_FontData: m_FontData:
m_Font: {fileID: 12800000, guid: d07e6c2670f164cf7939ab011061a9bf, type: 3} m_Font: {fileID: 12800000, guid: 70069a8db1df148ae8b4c8108c079acf, type: 3}
m_FontSize: 16 m_FontSize: 16
m_FontStyle: 0 m_FontStyle: 0
m_BestFit: 0 m_BestFit: 0
@ -1418,7 +1418,7 @@ MonoBehaviour:
m_EditorClassIdentifier: m_EditorClassIdentifier:
m_Interval: 0.5 m_Interval: 0.5
m_Precision: 2 m_Precision: 2
m_Font: {fileID: 12800000, guid: d07e6c2670f164cf7939ab011061a9bf, type: 3} m_Font: {fileID: 12800000, guid: 70069a8db1df148ae8b4c8108c079acf, type: 3}
m_Opened: 1 m_Opened: 1
m_FoldoutObject: {fileID: 8720459857733317289} m_FoldoutObject: {fileID: 8720459857733317289}
m_OpenButton: {fileID: 2149219299373497733} m_OpenButton: {fileID: 2149219299373497733}
@ -1502,7 +1502,7 @@ MonoBehaviour:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
m_FontData: m_FontData:
m_Font: {fileID: 12800000, guid: d07e6c2670f164cf7939ab011061a9bf, type: 3} m_Font: {fileID: 12800000, guid: 70069a8db1df148ae8b4c8108c079acf, type: 3}
m_FontSize: 16 m_FontSize: 16
m_FontStyle: 0 m_FontStyle: 0
m_BestFit: 0 m_BestFit: 0
@ -1695,7 +1695,7 @@ MonoBehaviour:
m_PersistentCalls: m_PersistentCalls:
m_Calls: [] m_Calls: []
m_FontData: m_FontData:
m_Font: {fileID: 12800000, guid: d07e6c2670f164cf7939ab011061a9bf, type: 3} m_Font: {fileID: 12800000, guid: 70069a8db1df148ae8b4c8108c079acf, type: 3}
m_FontSize: 14 m_FontSize: 14
m_FontStyle: 0 m_FontStyle: 0
m_BestFit: 0 m_BestFit: 0

View File

@ -1,11 +1,10 @@
using System; using System;
using UnityEngine; using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.Serialization; using UnityEngine.Serialization;
using UnityEngine.UI; using UnityEngine.UI;
#if UNITY_EDITOR #if UNITY_EDITOR
using UnityEditor; using UnityEditor;
using UnityEditorInternal;
#endif #endif
namespace Coffee.NanoMonitor namespace Coffee.NanoMonitor
@ -14,29 +13,24 @@ namespace Coffee.NanoMonitor
[CustomEditor(typeof(NanoMonitor))] [CustomEditor(typeof(NanoMonitor))]
internal class NanoMonitorEditor : Editor internal class NanoMonitorEditor : Editor
{ {
private ReorderableList _monitorItemList; private UnityEditorInternal.ReorderableList m_MonitorItemList;
private void OnEnable() private void OnEnable()
{ {
var items = serializedObject.FindProperty("m_CustomMonitorItems"); var items = serializedObject.FindProperty("m_CustomMonitorItems");
_monitorItemList = new ReorderableList(serializedObject, items) m_MonitorItemList = new UnityEditorInternal.ReorderableList(serializedObject, items)
{ {
draggable = false, draggable = false,
drawHeaderCallback = r => drawHeaderCallback = r => { EditorGUI.LabelField(r, new GUIContent("Custom Monitor Items")); },
{
EditorGUI.LabelField(r, new GUIContent("Custom Monitor Items"));
},
drawElementCallback = (r, i, _, __) => drawElementCallback = (r, i, _, __) =>
{ {
EditorGUI.LabelField(new Rect(r.x, r.y, r.width, r.height - 2), GUIContent.none, EditorGUI.LabelField(new Rect(r.x, r.y, r.width, r.height - 2), GUIContent.none, EditorStyles.textArea);
EditorStyles.textArea);
var labelWidth = EditorGUIUtility.labelWidth; var labelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 80; EditorGUIUtility.labelWidth = 80;
EditorGUI.PropertyField(new Rect(r.x + 2, r.y + 3, r.width - 4, r.height - 4), EditorGUI.PropertyField(new Rect(r.x + 2, r.y + 3, r.width - 4, r.height - 4), items.GetArrayElementAtIndex(i), true);
items.GetArrayElementAtIndex(i), true);
EditorGUIUtility.labelWidth = labelWidth; EditorGUIUtility.labelWidth = labelWidth;
}, },
elementHeightCallback = i => EditorGUI.GetPropertyHeight(items.GetArrayElementAtIndex(i)) + 6 elementHeightCallback = i => EditorGUI.GetPropertyHeight(items.GetArrayElementAtIndex(i)) + 6,
}; };
} }
@ -44,7 +38,7 @@ namespace Coffee.NanoMonitor
{ {
base.OnInspectorGUI(); base.OnInspectorGUI();
_monitorItemList.DoLayoutList(); m_MonitorItemList.DoLayoutList();
serializedObject.ApplyModifiedProperties(); serializedObject.ApplyModifiedProperties();
} }
} }
@ -58,50 +52,38 @@ namespace Coffee.NanoMonitor
// Serialize Members. // Serialize Members.
//################################ //################################
// Settings // Settings
[Header("Settings")] [Header("Settings")] [SerializeField] [Range(0.01f, 2f)]
[SerializeField]
[Range(0.01f, 2f)]
private float m_Interval = 1f; private float m_Interval = 1f;
[SerializeField] [SerializeField] [Range(0, 3)] private int m_Precision = 2;
[Range(0, 3)] [SerializeField] private Font m_Font;
private int m_Precision = 2;
[SerializeField]
private Font m_Font;
// Foldout // Foldout
[Header("Foldout")] [Header("Foldout")] [SerializeField] private bool m_Opened = false;
[SerializeField]
private bool m_Opened;
[FormerlySerializedAs("m_Collapse")] [FormerlySerializedAs("m_Collapse")] [SerializeField]
[SerializeField] private GameObject m_FoldoutObject = null;
private GameObject m_FoldoutObject;
[SerializeField] [SerializeField] private Button m_OpenButton = null;
private Button m_OpenButton; [SerializeField] private Button m_CloseButton = null;
[SerializeField]
private Button m_CloseButton;
// View // View
[Header("View")] [Header("View")] [SerializeField] private MonitorUI m_Fps = null;
[SerializeField] [SerializeField] private MonitorUI m_Gc = null;
private MonitorUI m_Fps; [SerializeField] private MonitorUI m_MonoUsage = null;
[SerializeField] private MonitorUI m_UnityUsage = null;
[SerializeField] [HideInInspector] [SerializeField] private CustomMonitorItem[] m_CustomMonitorItems = new CustomMonitorItem[0];
private MonitorUI m_Gc;
[SerializeField]
private MonitorUI m_MonoUsage;
[SerializeField] //################################
private MonitorUI m_UnityUsage; // Public Members.
//################################
[HideInInspector] public void Clean()
[SerializeField] {
private CustomMonitorItem[] m_CustomMonitorItems = new CustomMonitorItem[0]; Resources.UnloadUnusedAssets();
GC.Collect();
}
//################################ //################################
@ -111,75 +93,44 @@ namespace Coffee.NanoMonitor
private double _fpsElapsed; private double _fpsElapsed;
private int _frames; private int _frames;
private void Update() private void Open()
{ {
_frames++;
_elapsed += Time.unscaledDeltaTime;
_fpsElapsed += Time.unscaledDeltaTime;
if (_elapsed < m_Interval) return;
if (m_Fps)
{
m_Fps.SetText("FPS: {0}", (int)(_frames / _fpsElapsed));
}
if (m_Gc)
{
m_Gc.SetText("GC: {0}", GC.CollectionCount(0));
}
if (m_MonoUsage)
{
var monoUsed = (Profiler.GetMonoUsedSizeLong() >> 10) / 1024f;
var monoTotal = (Profiler.GetMonoHeapSizeLong() >> 10) / 1024f;
if (m_Precision == 3)
{
m_MonoUsage.SetText("Mono: {0:N3}/{1:N3}MB", monoUsed, monoTotal);
}
else if (m_Precision == 2)
{
m_MonoUsage.SetText("Mono: {0:N2}/{1:N2}MB", monoUsed, monoTotal);
}
else if (m_Precision == 1)
{
m_MonoUsage.SetText("Mono: {0:N1}/{1:N1}MB", monoUsed, monoTotal);
}
else
{
m_MonoUsage.SetText("Mono: {0:N0}/{1:N0}MB", monoUsed, monoTotal);
}
}
if (m_UnityUsage)
{
var unityUsed = (Profiler.GetTotalAllocatedMemoryLong() >> 10) / 1024f;
var unityTotal = (Profiler.GetTotalReservedMemoryLong() >> 10) / 1024f;
if (m_Precision == 3)
{
m_UnityUsage.SetText("Unity: {0:N3}/{1:N3}MB", unityUsed, unityTotal);
}
else if (m_Precision == 2)
{
m_UnityUsage.SetText("Unity: {0:N2}/{1:N2}MB", unityUsed, unityTotal);
}
else if (m_Precision == 1)
{
m_UnityUsage.SetText("Unity: {0:N1}/{1:N1}MB", unityUsed, unityTotal);
}
else
{
m_UnityUsage.SetText("Unity: {0:N0}/{1:N0}MB", unityUsed, unityTotal);
}
}
foreach (var item in m_CustomMonitorItems)
{
item.UpdateText();
}
_frames = 0; _frames = 0;
_elapsed %= m_Interval; _elapsed = m_Interval;
_fpsElapsed = 0; _fpsElapsed = 0;
if (m_FoldoutObject)
{
m_FoldoutObject.SetActive(true);
}
if (m_CloseButton)
{
m_CloseButton.gameObject.SetActive(true);
}
if (m_OpenButton)
{
m_OpenButton.gameObject.SetActive(false);
}
}
private void Close()
{
if (m_FoldoutObject)
{
m_FoldoutObject.SetActive(false);
}
if (m_CloseButton)
{
m_CloseButton.gameObject.SetActive(false);
}
if (m_OpenButton)
{
m_OpenButton.gameObject.SetActive(true);
}
} }
@ -221,6 +172,77 @@ namespace Coffee.NanoMonitor
} }
} }
private void Update()
{
_frames++;
_elapsed += Time.unscaledDeltaTime;
_fpsElapsed += Time.unscaledDeltaTime;
if (_elapsed < m_Interval) return;
if (m_Fps)
{
m_Fps.SetText("FPS: {0}", (int) (_frames / _fpsElapsed));
}
if (m_Gc)
{
m_Gc.SetText("GC: {0}", GC.CollectionCount(0));
}
if (m_MonoUsage)
{
var monoUsed = (UnityEngine.Profiling.Profiler.GetMonoUsedSizeLong() >> 10) / 1024f;
var monoTotal = (UnityEngine.Profiling.Profiler.GetMonoHeapSizeLong() >> 10) / 1024f;
if (m_Precision == 3)
{
m_MonoUsage.SetText("Mono: {0:N3}/{1:N3}MB", monoUsed, monoTotal);
}
else if (m_Precision == 2)
{
m_MonoUsage.SetText("Mono: {0:N2}/{1:N2}MB", monoUsed, monoTotal);
}
else if (m_Precision == 1)
{
m_MonoUsage.SetText("Mono: {0:N1}/{1:N1}MB", monoUsed, monoTotal);
}
else
{
m_MonoUsage.SetText("Mono: {0:N0}/{1:N0}MB", monoUsed, monoTotal);
}
}
if (m_UnityUsage)
{
var unityUsed = (UnityEngine.Profiling.Profiler.GetTotalAllocatedMemoryLong() >> 10) / 1024f;
var unityTotal = (UnityEngine.Profiling.Profiler.GetTotalReservedMemoryLong() >> 10) / 1024f;
if (m_Precision == 3)
{
m_UnityUsage.SetText("Unity: {0:N3}/{1:N3}MB", unityUsed, unityTotal);
}
else if (m_Precision == 2)
{
m_UnityUsage.SetText("Unity: {0:N2}/{1:N2}MB", unityUsed, unityTotal);
}
else if (m_Precision == 1)
{
m_UnityUsage.SetText("Unity: {0:N1}/{1:N1}MB", unityUsed, unityTotal);
}
else
{
m_UnityUsage.SetText("Unity: {0:N0}/{1:N0}MB", unityUsed, unityTotal);
}
}
foreach (var item in m_CustomMonitorItems)
{
item.UpdateText();
}
_frames = 0;
_elapsed %= m_Interval;
_fpsElapsed = 0;
}
#if UNITY_EDITOR #if UNITY_EDITOR
private void OnValidate() private void OnValidate()
{ {
@ -242,55 +264,5 @@ namespace Coffee.NanoMonitor
} }
} }
#endif #endif
//################################
// Public Members.
//################################
public void Clean()
{
Resources.UnloadUnusedAssets();
GC.Collect();
}
private void Open()
{
_frames = 0;
_elapsed = m_Interval;
_fpsElapsed = 0;
if (m_FoldoutObject)
{
m_FoldoutObject.SetActive(true);
}
if (m_CloseButton)
{
m_CloseButton.gameObject.SetActive(true);
}
if (m_OpenButton)
{
m_OpenButton.gameObject.SetActive(false);
}
}
private void Close()
{
if (m_FoldoutObject)
{
m_FoldoutObject.SetActive(false);
}
if (m_CloseButton)
{
m_CloseButton.gameObject.SetActive(false);
}
if (m_OpenButton)
{
m_OpenButton.gameObject.SetActive(true);
}
}
} }
} }

View File

@ -2,17 +2,14 @@
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
namespace Coffee.NanoMonitor namespace Coffee.NanoMonitor{
{
public class FixedFont public class FixedFont
{ {
private const string k_Characters = private readonly UIVertex[] _tmpVerts = new UIVertex[4];
"_!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; private static readonly Dictionary<Font, FixedFont> _fonts = new Dictionary<Font, FixedFont>();
private static readonly TextGenerator _textGenerator = new TextGenerator(100);
private static readonly Dictionary<Font, FixedFont> s_Fonts = new Dictionary<Font, FixedFont>(); private static TextGenerationSettings _settings = new TextGenerationSettings
private static readonly TextGenerator s_TextGenerator = new TextGenerator(100);
private static TextGenerationSettings s_Settings = new TextGenerationSettings
{ {
scaleFactor = 1, scaleFactor = 1,
horizontalOverflow = HorizontalWrapMode.Overflow, horizontalOverflow = HorizontalWrapMode.Overflow,
@ -22,33 +19,25 @@ namespace Coffee.NanoMonitor
color = Color.white color = Color.white
}; };
private int _resolution = 0;
private readonly Font _font; private readonly Font _font;
private readonly UIVertex[] _tmpVerts = new UIVertex[4];
private UICharInfo[] _charInfos;
private int _resolution;
private UIVertex[] _verts; private UIVertex[] _verts;
private UICharInfo[] _charInfos;
private FixedFont(Font font) private FixedFont(Font font)
{ {
_font = font; _font = font;
} }
public int fontSize public int fontSize => _font.dynamic ? 32 : _font.fontSize;
{
get
{
return _font.dynamic ? 32 : _font.fontSize;
}
}
public static FixedFont GetOrCreate(Font font) public static FixedFont GetOrCreate(Font font)
{ {
if (font == null) return null; if (font == null) return null;
if (s_Fonts.TryGetValue(font, out var data)) return data; if (_fonts.TryGetValue(font, out var data)) return data;
data = new FixedFont(font); data = new FixedFont(font);
s_Fonts.Add(font, data); _fonts.Add(font, data);
return data; return data;
} }
@ -72,20 +61,18 @@ namespace Coffee.NanoMonitor
if (_resolution == currentResolution) return; if (_resolution == currentResolution) return;
_resolution = currentResolution; _resolution = currentResolution;
s_Settings.font = _font; _settings.font = _font;
s_TextGenerator.Invalidate(); _textGenerator.Invalidate();
s_TextGenerator.Populate(k_Characters, s_Settings); _textGenerator.Populate("_!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", _settings);
_verts = s_TextGenerator.GetVerticesArray(); _verts = _textGenerator.GetVerticesArray();
_charInfos = s_TextGenerator.GetCharactersArray(); _charInfos = _textGenerator.GetCharactersArray();
float offsetX = 0; float offsetX = 0;
for (var i = 0; i < _verts.Length; i++) for (var i = 0; i < _verts.Length; i++)
{ {
if ((i & 3) == 0) if ((i & 3) == 0)
{
offsetX = _verts[i].position.x; offsetX = _verts[i].position.x;
}
var v = _verts[i]; var v = _verts[i];
v.position -= new Vector3(offsetX, 0); v.position -= new Vector3(offsetX, 0);
@ -131,27 +118,18 @@ namespace Coffee.NanoMonitor
for (var i = 0; i < 4; i++) for (var i = 0; i < 4; i++)
{ {
_tmpVerts[i] = new UIVertex _tmpVerts[i] = new UIVertex();
{ _tmpVerts[i].uv0 = uv;
uv0 = uv, _tmpVerts[i].color = color;
color = color
};
switch (i) if (i == 0)
{
case 0:
_tmpVerts[i].position = new Vector2(-size.x, -size.y) + offset; _tmpVerts[i].position = new Vector2(-size.x, -size.y) + offset;
break; else if (i == 1)
case 1:
_tmpVerts[i].position = new Vector2(-size.x, size.y) + offset; _tmpVerts[i].position = new Vector2(-size.x, size.y) + offset;
break; else if (i == 2)
case 2:
_tmpVerts[i].position = new Vector2(size.x, size.y) + offset; _tmpVerts[i].position = new Vector2(size.x, size.y) + offset;
break; else
case 3:
_tmpVerts[i].position = new Vector2(size.x, -size.y) + offset; _tmpVerts[i].position = new Vector2(size.x, -size.y) + offset;
break;
}
} }
toFill.AddUIVertexQuad(_tmpVerts); toFill.AddUIVertexQuad(_tmpVerts);

View File

@ -1,46 +1,50 @@
using System.Text; using System.Text;
using UnityEditor;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
namespace Coffee.NanoMonitor namespace Coffee.NanoMonitor{
{
#if UNITY_EDITOR #if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(MonitorUI))] [CustomEditor(typeof(MonitorUI))]
public class MonitorTextEditor : Editor public class MonitorTextEditor : Editor
{ {
private SerializedProperty _color; private SerializedProperty m_Mode;
private SerializedProperty _font; private SerializedProperty m_TextAnchor;
private SerializedProperty _fontSize; private SerializedProperty m_FontSize;
private SerializedProperty _mode; private SerializedProperty m_Font;
private SerializedProperty _text; private SerializedProperty m_Text;
private SerializedProperty _textAnchor; private SerializedProperty m_Color;
private void OnEnable() private void OnEnable()
{ {
_mode = serializedObject.FindProperty("m_Mode"); m_Mode = serializedObject.FindProperty("m_Mode");
_text = serializedObject.FindProperty("m_Text");
_color = serializedObject.FindProperty("m_Color"); m_Text = serializedObject.FindProperty("m_Text");
m_Color = serializedObject.FindProperty("m_Color");
var fontData = serializedObject.FindProperty("m_FontData"); var fontData = serializedObject.FindProperty("m_FontData");
_fontSize = fontData.FindPropertyRelative("m_FontSize"); m_FontSize = fontData.FindPropertyRelative("m_FontSize");
_font = fontData.FindPropertyRelative("m_Font"); m_Font = fontData.FindPropertyRelative("m_Font");
_textAnchor = serializedObject.FindProperty("m_TextAnchor"); m_TextAnchor = serializedObject.FindProperty("m_TextAnchor");
} }
public override void OnInspectorGUI() public override void OnInspectorGUI()
{ {
serializedObject.Update(); serializedObject.Update();
EditorGUILayout.PropertyField(_mode); EditorGUILayout.PropertyField(m_Mode);
if ((MonitorUI.Mode)_mode.intValue == MonitorUI.Mode.Text) if ((MonitorUI.Mode) m_Mode.intValue == MonitorUI.Mode.Text)
{ {
EditorGUILayout.PropertyField(_text); EditorGUILayout.PropertyField(m_Text);
EditorGUILayout.PropertyField(_fontSize); EditorGUILayout.PropertyField(m_FontSize);
EditorGUILayout.PropertyField(_textAnchor); EditorGUILayout.PropertyField(m_TextAnchor);
EditorGUILayout.PropertyField(_font);
//EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.PropertyField(m_Font);
//EditorGUI.EndDisabledGroup();
} }
EditorGUILayout.PropertyField(_color); EditorGUILayout.PropertyField(m_Color);
serializedObject.ApplyModifiedProperties(); serializedObject.ApplyModifiedProperties();
} }
@ -52,7 +56,7 @@ namespace Coffee.NanoMonitor
public enum Mode public enum Mode
{ {
Text, Text,
Fill Fill,
} }
public enum TextAnchor public enum TextAnchor
@ -70,38 +74,26 @@ namespace Coffee.NanoMonitor
[SerializeField] private TextAnchor m_TextAnchor; [SerializeField] private TextAnchor m_TextAnchor;
//################################
// Private Members.
//################################
private readonly StringBuilder _sb = new StringBuilder(64);
//################################ //################################
// Public Members. // Public Members.
//################################ //################################
public override string text public override string text
{ {
get get => m_StringBuilder.ToString();
{
return _sb.ToString();
}
set set
{ {
m_Text = value; m_Text = value;
if (_sb.IsEqual(m_Text)) return; if (m_StringBuilder.IsEqual(m_Text)) return;
_sb.Length = 0; m_StringBuilder.Length = 0;
_sb.Append(m_Text); m_StringBuilder.Append(m_Text);
SetVerticesDirty(); SetVerticesDirty();
} }
} }
public TextAnchor textAnchor public TextAnchor textAnchor
{ {
get get => m_TextAnchor;
{
return m_TextAnchor;
}
set set
{ {
if (m_TextAnchor == value) return; if (m_TextAnchor == value) return;
@ -113,30 +105,65 @@ namespace Coffee.NanoMonitor
public override bool raycastTarget public override bool raycastTarget
{ {
get get => m_Mode == Mode.Fill;
{
return m_Mode == Mode.Fill;
}
set { } set { }
} }
public void SetText(string format, double arg0 = 0, double arg1 = 0, double arg2 = 0, double arg3 = 0)
{
m_StringBuilder.Length = 0;
m_StringBuilder.AppendFormatNoAlloc(format, arg0, arg1, arg2, arg3);
SetVerticesDirty();
}
public void SetText(StringBuilder builder)
{
m_StringBuilder.Length = 0;
m_StringBuilder.Append(builder);
SetVerticesDirty();
}
//################################
// Private Members.
//################################
private readonly StringBuilder m_StringBuilder = new StringBuilder(64);
private void UpdateFont()
{
//var globalFont = NNanoMonitorttings.Instance.font;
//if (globalFont)
//{
// font = globalFont;
//}
var fontData = FixedFont.GetOrCreate(font);
if (fontData != null)
{
fontData.Invalidate();
fontData.UpdateFont();
}
}
//################################ //################################
// Unity Callbacks. // Unity Callbacks.
//################################ //################################
protected override void OnEnable() protected override void OnEnable()
{ {
//NaNanoMonitortings.Instance.onFontChanged += UpdateFont;
RegisterDirtyMaterialCallback(UpdateFont); RegisterDirtyMaterialCallback(UpdateFont);
base.OnEnable(); base.OnEnable();
raycastTarget = false; raycastTarget = false;
maskable = false; maskable = false;
_sb.Length = 0; m_StringBuilder.Length = 0;
_sb.Append(m_Text); m_StringBuilder.Append(m_Text);
} }
protected override void OnDisable() protected override void OnDisable()
{ {
//NanNanoMonitorings.Instance.onFontChanged -= UpdateFont;
UnregisterDirtyMaterialCallback(UpdateFont); UnregisterDirtyMaterialCallback(UpdateFont);
base.OnDisable(); base.OnDisable();
@ -147,38 +174,14 @@ namespace Coffee.NanoMonitor
{ {
base.OnValidate(); base.OnValidate();
if (!_sb.IsEqual(m_Text)) if (!m_StringBuilder.IsEqual(m_Text))
{ {
_sb.Length = 0; m_StringBuilder.Length = 0;
_sb.Append(m_Text); m_StringBuilder.Append(m_Text);
} }
} }
#endif #endif
public void SetText(string format, double arg0 = 0, double arg1 = 0, double arg2 = 0, double arg3 = 0)
{
_sb.Length = 0;
_sb.AppendFormatNoAlloc(format, arg0, arg1, arg2, arg3);
SetVerticesDirty();
}
public void SetText(StringBuilder builder)
{
_sb.Length = 0;
_sb.Append(builder);
SetVerticesDirty();
}
private void UpdateFont()
{
var fontData = FixedFont.GetOrCreate(font);
if (fontData != null)
{
fontData.Invalidate();
fontData.UpdateFont();
}
}
protected override void OnPopulateMesh(VertexHelper toFill) protected override void OnPopulateMesh(VertexHelper toFill)
{ {
toFill.Clear(); toFill.Clear();
@ -194,7 +197,7 @@ namespace Coffee.NanoMonitor
return; return;
} }
var scale = (float)fontSize / fontData.fontSize; var scale = (float) fontSize / fontData.fontSize;
float offset = 0; float offset = 0;
switch (textAnchor) switch (textAnchor)
{ {
@ -202,26 +205,20 @@ namespace Coffee.NanoMonitor
offset = rectTransform.rect.xMin; offset = rectTransform.rect.xMin;
break; break;
case TextAnchor.Center: case TextAnchor.Center:
for (var i = 0; i < _sb.Length; i++) for (var i = 0; i < m_StringBuilder.Length; i++)
{ offset = fontData.Layout(m_StringBuilder[i], offset, scale);
offset = fontData.Layout(_sb[i], offset, scale);
}
offset = -offset / 2; offset = -offset / 2;
break; break;
case TextAnchor.Right: case TextAnchor.Right:
for (var i = 0; i < _sb.Length; i++) for (var i = 0; i < m_StringBuilder.Length; i++)
{ offset = fontData.Layout(m_StringBuilder[i], offset, scale);
offset = fontData.Layout(_sb[i], offset, scale);
}
offset = rectTransform.rect.xMax - offset; offset = rectTransform.rect.xMax - offset;
break; break;
} }
for (var i = 0; i < _sb.Length; i++) for (var i = 0; i < m_StringBuilder.Length; i++)
{ {
offset = fontData.Append(toFill, _sb[i], offset, scale, color); offset = fontData.Append(toFill, m_StringBuilder[i], offset, scale, color);
} }
} }

View File

@ -0,0 +1,55 @@
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Coffee.MiniProfiler
{
internal class MiniProfilerSettings : ScriptableSettings<MiniProfilerSettings>
{
[Header("UI")]
[SerializeField] private Font m_Font = null;
[Header("Instantiate On Boot")]
[SerializeField] private GameObject miniProfilerPrefab = null;
[SerializeField] private string[] m_SceneNames = new string[0];
public Font font => m_Font;
public event Action onFontChanged;
protected override void OnBoot()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
SceneManager.sceneLoaded += OnSceneLoaded;
}
public void OnSceneLoaded(Scene scene, LoadSceneMode __)
{
SceneManager.sceneLoaded -= OnSceneLoaded;
if (!miniProfilerPrefab || !m_SceneNames.Contains(scene.name)) return;
var go = Instantiate(miniProfilerPrefab);
if (Application.isPlaying)
DontDestroyOnLoad(go);
}
protected override bool bootOnPlayMode => true;
protected override bool bootOnEditorMode => false;
#if UNITY_EDITOR
protected override void OnValidate()
{
onFontChanged?.Invoke();
base.OnValidate();
}
private void Reset()
{
m_Font = UnityEngine.Resources.GetBuiltinResource<Font>("Arial.ttf");
}
[UnityEditor.SettingsProvider]
private static UnityEditor.SettingsProvider CreateSettingsProvider() => new ScriptableSettingsProvider<MiniProfilerSettings>();
#endif
}
}

View File

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 647dad4f8226a49b9874b68b79413d64
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- miniProfilerPrefab: {fileID: 7211429669315726685, guid: 784696794bc6345bc80bf49623581c2e,
type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,280 @@
using UnityEngine;
using System;
using System.Linq;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Coffee.MiniProfiler
{
internal abstract class ScriptableSettings : ScriptableObject
{
#if UNITY_EDITOR
public virtual string GetSettingPath()
{
return GetSettingPath(GetType());
}
internal static string GetSettingPath(Type type)
{
return $"Project/{ObjectNames.NicifyVariableName(type.Name.Replace("ProjectSettings", ""))}";
}
#endif
public virtual string GetDefaultAssetPath()
{
return $"Assets/ProjectSettings/{GetType().Name}.asset";
}
}
internal abstract class ScriptableSettings<T> : ScriptableSettings
where T : ScriptableSettings<T>
{
//################################
// Public Members.
//################################
public static T Instance => _instance ? _instance : _instance = GetOrCreate();
private static T _instance;
protected virtual bool bootOnEditorMode => true;
protected virtual bool bootOnPlayMode => true;
protected abstract void OnBoot();
#if UNITY_EDITOR
//################################
// Private Members.
//################################
private bool enabled;
private static T GetOrCreate()
{
return PlayerSettings.GetPreloadedAssets()
.OfType<T>()
.FirstOrDefault() ?? CreateInstance<T>();
}
private void OnPlayModeStateChanged(PlayModeStateChange state)
{
switch (state)
{
case PlayModeStateChange.EnteredEditMode:
enabled = true;
Boot();
break;
case PlayModeStateChange.ExitingEditMode:
case PlayModeStateChange.ExitingPlayMode:
enabled = false;
break;
}
}
private void Boot()
{
var preloadedAssets = PlayerSettings.GetPreloadedAssets();
var assets = preloadedAssets.OfType<T>().ToArray();
var first = assets.FirstOrDefault() ?? this as T;
// If there are no preloaded assets, registry the first asset.
// If there are multiple preloaded assets, unregisters all but the first one.
if (assets.Length != 1)
{
// The first asset is not saved.
if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(first)))
{
if (!AssetDatabase.IsValidFolder("Assets/ProjectSettings"))
AssetDatabase.CreateFolder("Assets", "ProjectSettings");
var assetName = ObjectNames.NicifyVariableName(first.GetType().Name.Replace("ProjectSettings", ""));
var assetPath = AssetDatabase.GenerateUniqueAssetPath($"Assets/ProjectSettings/{assetName}.asset");
AssetDatabase.CreateAsset(first, assetPath);
}
PlayerSettings.SetPreloadedAssets(preloadedAssets
.Where(x => x)
.Except(assets)
.Concat(new[] {first})
.ToArray());
}
// Another asset is registered as a preload asset.
if (first != this)
{
UnityEngine.Debug.LogError($"Another asset '{first}' is registered as a preload '{typeof(T).Name}' asset." +
$"\nThis instance ('{this}') will be ignored. Check 'Project Settings > Player > Preload Assets'", this);
return;
}
_instance = first;
// Do nothing on editor mode.
if (!bootOnEditorMode && !EditorApplication.isPlayingOrWillChangePlaymode)
{
return;
}
// Do nothing on play mode.
if (!bootOnPlayMode && EditorApplication.isPlayingOrWillChangePlaymode)
{
return;
}
OnBoot();
}
//################################
// Unity Callbacks.
//################################
private void OnEnable()
{
enabled = true;
Boot();
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
}
private void OnDisable()
{
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
}
protected virtual void OnValidate()
{
if (enabled)
Boot();
}
#else
private static T GetOrCreate()
{
return CreateInstance<T>();
}
private void OnEnable()
{
if (!bootOnPlayMode) return;
_instance = this as T;
OnBoot();
}
#endif
}
[CustomEditor(typeof(ScriptablePreferenceSettings<>), true)]
internal class ScriptablePreferenceSettingsEditor : Editor
{
private static readonly GUIContent _button = new GUIContent("Open Project Settings");
public override void OnInspectorGUI()
{
if (GUILayout.Button(_button))
{
SettingsService.OpenProjectSettings(((ScriptableSettings) target).GetSettingPath());
}
}
}
internal abstract class ScriptablePreferenceSettings<T> : ScriptableSingleton<T>
where T : ScriptablePreferenceSettings<T>
{
}
#if UNITY_EDITOR
[CustomEditor(typeof(ScriptableSettings), true)]
internal class ScriptableSettingsEditor : Editor
{
private static readonly GUIContent _button = new GUIContent("Open Project Settings");
public override void OnInspectorGUI()
{
if (GUILayout.Button(_button))
{
SettingsService.OpenProjectSettings(((ScriptableSettings) target).GetSettingPath());
}
}
}
internal class ScriptableSettingsProvider<T> : SettingsProvider where T : ScriptableSettings<T>
{
public ScriptableSettingsProvider(string path) : base(path)
{
}
public ScriptableSettingsProvider() : base(ScriptableSettings.GetSettingPath(typeof(T)))
{
}
protected SerializedObject serializedObject { get; private set; }
protected ScriptableSettings<T> target { get; private set; }
public sealed override void OnGUI(string searchContext)
{
if (!target)
{
target = ScriptableSettings<T>.Instance;
serializedObject = new SerializedObject(target);
}
OnGUI();
}
protected virtual void OnGUI()
{
serializedObject.UpdateIfRequiredOrScript();
var iterator = serializedObject.GetIterator();
var enterChildren = true;
while (iterator.NextVisible(enterChildren))
{
if (iterator.propertyPath != "m_Script")
EditorGUILayout.PropertyField(iterator, true);
enterChildren = false;
}
serializedObject.ApplyModifiedProperties();
}
}
internal class ScriptablePreferenceSettingsProvider<T> : SettingsProvider where T : ScriptableSettings<T>
{
public ScriptablePreferenceSettingsProvider(string path) : base(path, SettingsScope.User)
{
}
public ScriptablePreferenceSettingsProvider() : base(ScriptableSettings.GetSettingPath(typeof(T)), SettingsScope.User)
{
}
protected SerializedObject serializedObject { get; private set; }
protected ScriptableSettings<T> target { get; private set; }
public sealed override void OnGUI(string searchContext)
{
if (!target)
{
target = ScriptableSettings<T>.Instance;
serializedObject = new SerializedObject(target);
}
OnGUI();
}
protected virtual void OnGUI()
{
serializedObject.UpdateIfRequiredOrScript();
var iterator = serializedObject.GetIterator();
var enterChildren = true;
while (iterator.NextVisible(enterChildren))
{
if (iterator.propertyPath != "m_Script")
EditorGUILayout.PropertyField(iterator, true);
enterChildren = false;
}
serializedObject.ApplyModifiedProperties();
}
}
#endif
}

View File

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

View File

@ -1,5 +1,4 @@
using System; using UnityEngine;
using UnityEngine;
#if UNITY_EDITOR #if UNITY_EDITOR
using UnityEditor; using UnityEditor;
#endif #endif
@ -12,17 +11,12 @@ namespace Coffee.NanoMonitor
{ {
public override void OnGUI(Rect p, SerializedProperty property, GUIContent label) public override void OnGUI(Rect p, SerializedProperty property, GUIContent label)
{ {
var pos = new Rect(p.x, p.y + 18 * 0, p.width, 16); EditorGUI.PropertyField(new Rect(p.x, p.y + 18 * 0, p.width, 16), property.FindPropertyRelative("m_Text"));
EditorGUI.PropertyField(pos, property.FindPropertyRelative("m_Text")); EditorGUI.PropertyField(new Rect(p.x, p.y + 18 * 1, p.width, 16), property.FindPropertyRelative("m_Format"));
pos.y += 18;
EditorGUI.PropertyField(pos, property.FindPropertyRelative("m_Format"));
pos.y += 18;
EditorGUI.indentLevel++; EditorGUI.indentLevel++;
EditorGUI.PropertyField(pos, property.FindPropertyRelative("m_Arg0")); EditorGUI.PropertyField(new Rect(p.x, p.y + 18 * 2, p.width, 16), property.FindPropertyRelative("m_Arg0"));
pos.y += 18; EditorGUI.PropertyField(new Rect(p.x, p.y + 18 * 3, p.width, 16), property.FindPropertyRelative("m_Arg1"));
EditorGUI.PropertyField(pos, property.FindPropertyRelative("m_Arg1")); EditorGUI.PropertyField(new Rect(p.x, p.y + 18 * 4, p.width, 16), property.FindPropertyRelative("m_Arg2"));
pos.y += 18;
EditorGUI.PropertyField(pos, property.FindPropertyRelative("m_Arg2"));
EditorGUI.indentLevel--; EditorGUI.indentLevel--;
property.serializedObject.ApplyModifiedProperties(); property.serializedObject.ApplyModifiedProperties();
@ -35,21 +29,19 @@ namespace Coffee.NanoMonitor
} }
#endif #endif
[Serializable] [System.Serializable]
public class CustomMonitorItem public class CustomMonitorItem
{ {
[SerializeField] private MonitorUI m_Text; [SerializeField] private MonitorUI m_Text = null;
[SerializeField] private string m_Format = ""; [SerializeField] private string m_Format = "";
[SerializeField] private NumericProperty m_Arg0; [SerializeField] private NumericProperty m_Arg0 = null;
[SerializeField] private NumericProperty m_Arg1; [SerializeField] private NumericProperty m_Arg1 = null;
[SerializeField] private NumericProperty m_Arg2; [SerializeField] private NumericProperty m_Arg2 = null;
public void UpdateText() public void UpdateText()
{ {
if (m_Text) if (m_Text)
{
m_Text.SetText(m_Format, m_Arg0.Get(), m_Arg1.Get(), m_Arg2.Get()); m_Text.SetText(m_Format, m_Arg0.Get(), m_Arg1.Get(), m_Arg2.Get());
} }
} }
}
} }

View File

@ -2,8 +2,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using UnityEditor;
using UnityEngine; using UnityEngine;
using UnityEngine.Serialization;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Coffee.NanoMonitor namespace Coffee.NanoMonitor
{ {
@ -16,45 +20,39 @@ namespace Coffee.NanoMonitor
private static readonly Dictionary<Type, string> s_SupportedTypes = new Dictionary<Type, string> private static readonly Dictionary<Type, string> s_SupportedTypes = new Dictionary<Type, string>
{ {
{ typeof(bool), "bool" }, {typeof(bool), "bool"},
{ typeof(sbyte), "sbyte" }, {typeof(sbyte), "sbyte"},
{ typeof(short), "short" }, {typeof(short), "short"},
{ typeof(int), "int" }, {typeof(int), "int"},
{ typeof(long), "long" }, {typeof(long), "long"},
{ typeof(byte), "byte" }, {typeof(byte), "byte"},
{ typeof(ushort), "ushort" }, {typeof(ushort), "ushort"},
{ typeof(uint), "uint" }, {typeof(uint), "uint"},
{ typeof(ulong), "ulong" }, {typeof(ulong), "ulong"},
{ typeof(float), "float" }, {typeof(float), "float"},
{ typeof(double), "double" }, {typeof(double), "double"},
{ typeof(decimal), "decimal" } {typeof(decimal), "decimal"},
}; };
private static void Init() private static void Init()
{ {
if (s_PropertyMenu != null) return; if (s_PropertyMenu != null) return;
const BindingFlags bindingFlags = BindingFlags.Public
| BindingFlags.NonPublic
| BindingFlags.Static
| BindingFlags.GetProperty;
var properties = AppDomain.CurrentDomain.GetAssemblies() var properties = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes()) .SelectMany(assembly => assembly.GetTypes())
.SelectMany(type => type.GetProperties(bindingFlags)) .SelectMany(type => type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetProperty))
.Where(pi => pi.GetMethod != null && s_SupportedTypes.ContainsKey(pi.PropertyType)) .Where(pi => pi.GetMethod != null && s_SupportedTypes.ContainsKey(pi.PropertyType))
.OrderBy(pi => ConvertToMenuItem(pi, false)) .OrderBy(pi => ConvertToMenuItem(pi, false))
.ToArray(); .ToArray();
s_PropertyMenu = new GenericMenu(); s_PropertyMenu = new GenericMenu();
s_PropertyMenu.AddItem(new GUIContent("No Property"), false, s_PropertyMenu.AddItem(new GUIContent("No Property"), false, arg => s_OnMenuSelected?.Invoke(arg as PropertyInfo), null);
arg => s_OnMenuSelected?.Invoke(arg as PropertyInfo), null); s_PropertyMenu.AddItem(new GUIContent("(Non Public Properties)/"), false, ()=>{});
s_PropertyMenu.AddItem(new GUIContent("(Non Public Properties)/"), false, () => { });
s_PropertyMenu.AddSeparator(""); s_PropertyMenu.AddSeparator("");
foreach (var pi in properties) foreach (var pi in properties)
{ {
s_PropertyMenu.AddItem(new GUIContent(ConvertToMenuItem(pi, true)), false, s_PropertyMenu.AddItem(new GUIContent(ConvertToMenuItem(pi, true)), false, arg => s_OnMenuSelected?.Invoke(arg as PropertyInfo), pi);
arg => s_OnMenuSelected?.Invoke(arg as PropertyInfo), pi);
} }
} }
@ -62,17 +60,11 @@ namespace Coffee.NanoMonitor
{ {
var type = p.DeclaringType; var type = p.DeclaringType;
if (type == null) return ""; if (type == null) return "";
var category = p.GetMethod.IsPublic && type.IsPublic ? "" : "(Non Public Properties)/";
var category = p.GetMethod.IsPublic && type.IsPublic
? ""
: "(Non Public Properties)/";
var typeName = type.FullName; var typeName = type.FullName;
var asmName = type.Assembly.GetName().Name; var asmName = type.Assembly.GetName().Name;
if (asmName == "UnityEngine.CoreModule") if (asmName == "UnityEngine.CoreModule")
{
asmName = "UnityEngine"; asmName = "UnityEngine";
}
return propertyType return propertyType
? $"{category}{asmName}/{typeName}/{s_SupportedTypes[p.PropertyType]} {p.Name}" ? $"{category}{asmName}/{typeName}/{s_SupportedTypes[p.PropertyType]} {p.Name}"
: $"{category}{asmName}/{typeName}/{p.Name}"; : $"{category}{asmName}/{typeName}/{p.Name}";
@ -92,9 +84,7 @@ namespace Coffee.NanoMonitor
{ {
s_OnMenuSelected = p => s_OnMenuSelected = p =>
{ {
path.stringValue = p == null path.stringValue = p == null ? "" : $"{p.DeclaringType?.FullName}, {p.DeclaringType?.Assembly.GetName().Name};{p.Name}";
? ""
: $"{p.DeclaringType?.FullName}, {p.DeclaringType?.Assembly.GetName().Name};{p.Name}";
property.serializedObject.ApplyModifiedProperties(); property.serializedObject.ApplyModifiedProperties();
}; };
s_PropertyMenu.DropDown(position); s_PropertyMenu.DropDown(position);
@ -114,22 +104,6 @@ namespace Coffee.NanoMonitor
[SerializeField] private string m_Path = ""; [SerializeField] private string m_Path = "";
//################################
// Private Members.
//################################
private Func<double> _get;
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
}
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
var pInfo = GetPropertyInfo(m_Path);
_get = CreateFunc(pInfo?.GetMethod);
}
//################################ //################################
// Public Members. // Public Members.
//################################ //################################
@ -138,6 +112,12 @@ namespace Coffee.NanoMonitor
return _get?.Invoke() ?? -1; return _get?.Invoke() ?? -1;
} }
//################################
// Private Members.
//################################
private Func<double> _get = null;
private static PropertyInfo GetPropertyInfo(string path) private static PropertyInfo GetPropertyInfo(string path)
{ {
var p = path.Split(';'); var p = path.Split(';');
@ -146,14 +126,14 @@ namespace Coffee.NanoMonitor
var type = Type.GetType(p[0]); var type = Type.GetType(p[0]);
if (type == null) if (type == null)
{ {
Debug.LogException(new Exception($"Type '{p[0]}' is not found")); UnityEngine.Debug.LogException(new Exception($"Type '{p[0]}' is not found"));
return null; return null;
} }
var pInfo = type.GetProperty(p[1], BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Static); var pInfo = type.GetProperty(p[1], BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Static);
if (pInfo == null) if (pInfo == null)
{ {
Debug.LogException(new Exception($"Member '{p[1]}' is not found in type '{type}'")); UnityEngine.Debug.LogException(new Exception($"Member '{p[1]}' is not found in type '{type}'"));
} }
return pInfo; return pInfo;
@ -165,72 +145,55 @@ namespace Coffee.NanoMonitor
switch (Type.GetTypeCode(mInfo.ReturnType)) switch (Type.GetTypeCode(mInfo.ReturnType))
{ {
case TypeCode.Boolean: case TypeCode.Boolean:
{ var f_bool = (Func<bool>) mInfo.CreateDelegate(typeof(Func<bool>));
var func = (Func<bool>)mInfo.CreateDelegate(typeof(Func<bool>)); return () => f_bool() ? 1 : 0;
return () => func() ? 1 : 0;
}
case TypeCode.Byte: case TypeCode.Byte:
{ var f_byte = (Func<byte>) mInfo.CreateDelegate(typeof(Func<byte>));
var func = (Func<byte>)mInfo.CreateDelegate(typeof(Func<byte>)); return () => f_byte();
return () => func();
}
case TypeCode.SByte: case TypeCode.SByte:
{ var f_sbyte = (Func<sbyte>) mInfo.CreateDelegate(typeof(Func<sbyte>));
var func = (Func<sbyte>)mInfo.CreateDelegate(typeof(Func<sbyte>)); return () => f_sbyte();
return () => func();
}
case TypeCode.UInt16: case TypeCode.UInt16:
{ var f_ushort = (Func<ushort>) mInfo.CreateDelegate(typeof(Func<ushort>));
var func = (Func<ushort>)mInfo.CreateDelegate(typeof(Func<ushort>)); return () => f_ushort();
return () => func();
}
case TypeCode.UInt32: case TypeCode.UInt32:
{ var f_uint = (Func<uint>) mInfo.CreateDelegate(typeof(Func<uint>));
var func = (Func<uint>)mInfo.CreateDelegate(typeof(Func<uint>)); return () => f_uint();
return () => func();
}
case TypeCode.UInt64: case TypeCode.UInt64:
{ var f_ulong = (Func<ulong>) mInfo.CreateDelegate(typeof(Func<ulong>));
var func = (Func<ulong>)mInfo.CreateDelegate(typeof(Func<ulong>)); return () => f_ulong();
return () => func();
}
case TypeCode.Int16: case TypeCode.Int16:
{ var f_short = (Func<short>) mInfo.CreateDelegate(typeof(Func<short>));
var func = (Func<short>)mInfo.CreateDelegate(typeof(Func<short>)); return () => f_short();
return () => func();
}
case TypeCode.Int32: case TypeCode.Int32:
{ var f_int = (Func<int>) mInfo.CreateDelegate(typeof(Func<int>));
var f = (Func<int>)mInfo.CreateDelegate(typeof(Func<int>)); return () => f_int();
return () => f();
}
case TypeCode.Int64: case TypeCode.Int64:
{ var f_long = (Func<long>) mInfo.CreateDelegate(typeof(Func<long>));
var f = (Func<long>)mInfo.CreateDelegate(typeof(Func<long>)); return () => f_long();
return () => f();
}
case TypeCode.Decimal: case TypeCode.Decimal:
{ var f_decimal = (Func<decimal>) mInfo.CreateDelegate(typeof(Func<decimal>));
var f = (Func<decimal>)mInfo.CreateDelegate(typeof(Func<decimal>)); return () => (double) f_decimal();
return () => (double)f();
}
case TypeCode.Double: case TypeCode.Double:
{ var f_double = (Func<double>) mInfo.CreateDelegate(typeof(Func<double>));
var f = (Func<double>)mInfo.CreateDelegate(typeof(Func<double>)); return f_double;
return f;
}
case TypeCode.Single: case TypeCode.Single:
{ var f_float = (Func<float>) mInfo.CreateDelegate(typeof(Func<float>));
var f = (Func<float>)mInfo.CreateDelegate(typeof(Func<float>)); return () => f_float();
return () => f();
}
default: default:
{ UnityEngine.Debug.LogException(new Exception(string.Format("Method '{1}.{0}' is not supported.", mInfo.Name, mInfo.DeclaringType)));
var message = $"Method '{mInfo.DeclaringType}.{mInfo.Name} ({mInfo.ReturnType})' is not supported.";
Debug.LogException(new Exception(message));
return null; return null;
} }
} }
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
}
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
var pInfo = GetPropertyInfo(m_Path);
_get = CreateFunc(pInfo?.GetMethod);
} }
} }
} }

View File

@ -21,8 +21,7 @@ namespace Coffee.NanoMonitor
return true; return true;
} }
public static void AppendFormatNoAlloc(this StringBuilder sb, string format, double arg0 = 0, double arg1 = 0, public static void AppendFormatNoAlloc(this StringBuilder sb, string format, double arg0 = 0, double arg1 = 0, double arg2 = 0, double arg3 = 0)
double arg2 = 0, double arg3 = 0)
{ {
for (var i = 0; i < format.Length; i++) for (var i = 0; i < format.Length; i++)
{ {
@ -57,7 +56,7 @@ namespace Coffee.NanoMonitor
} }
} }
private static void AppendInteger(this StringBuilder sb, double number, int padding, int alignment) internal static void AppendInteger(this StringBuilder sb, double number, int padding, int precision, int alignment)
{ {
number = Math.Truncate(number); number = Math.Truncate(number);
var sign = number < 0; var sign = number < 0;
@ -69,8 +68,8 @@ namespace Coffee.NanoMonitor
var n = Math.Truncate(number % 10); var n = Math.Truncate(number % 10);
number /= 10; number /= 10;
sb.Append((char)(n + 48)); sb.Append((char) (n + 48));
} while (1 <= number || sb.Length - startIndex < padding); } while (1 <= number || (sb.Length - startIndex) < padding);
if (sign) if (sign)
{ {
@ -83,12 +82,11 @@ namespace Coffee.NanoMonitor
sb.Alignment(alignment, startIndex, endIndex); sb.Alignment(alignment, startIndex, endIndex);
} }
private static void AppendDouble(this StringBuilder sb, double number, int padding, int precision, internal static void AppendDouble(this StringBuilder sb, double number, int padding, int precision, int alignment)
int alignment)
{ {
var integer = Math.Truncate(number); var integer = Math.Truncate(number);
var startIndex = sb.Length; var startIndex = sb.Length;
sb.AppendInteger(integer, padding, 0); sb.AppendInteger(integer, padding, precision, 0);
if (0 < precision) if (0 < precision)
{ {
@ -98,8 +96,8 @@ namespace Coffee.NanoMonitor
for (var p = 0; p < precision; p++) for (var p = 0; p < precision; p++)
{ {
number *= 10; number *= 10;
integer = (long)number; integer = (long) number;
sb.Append((char)(integer + 48)); sb.Append((char) (integer + 48));
number -= integer; number -= integer;
number = Math.Round(number, precision); number = Math.Round(number, precision);
} }
@ -108,18 +106,20 @@ namespace Coffee.NanoMonitor
sb.Alignment(alignment, startIndex, sb.Length - 1); sb.Alignment(alignment, startIndex, sb.Length - 1);
} }
private static void Reverse(this StringBuilder sb, int start, int end) internal static void Reverse(this StringBuilder sb, int start, int end)
{ {
while (start < end) while (start < end)
{ {
// swap var c = sb[start];
(sb[start], sb[end]) = (sb[end], sb[start]); sb[start] = sb[end];
sb[end] = c;
start++; start++;
end--; end--;
} }
} }
private static void Alignment(this StringBuilder sb, int alignment, int start, int end) internal static void Alignment(this StringBuilder sb, int alignment, int start, int end)
{ {
if (alignment == 0) return; if (alignment == 0) return;
@ -129,8 +129,9 @@ namespace Coffee.NanoMonitor
sb.Append(' ', alignment - len); sb.Append(' ', alignment - len);
for (var i = 0; i < len; i++) for (var i = 0; i < len; i++)
{ {
// swap var c = sb[end - i];
(sb[end - i], sb[start + alignment - i - 1]) = (sb[start + alignment - i - 1], sb[end - i]); sb[end - i] = sb[start + alignment - i - 1];
sb[start + alignment - i - 1] = c;
} }
} }
else if (alignment < 0 && len < -alignment) else if (alignment < 0 && len < -alignment)
@ -139,8 +140,7 @@ namespace Coffee.NanoMonitor
} }
} }
private static int GetFormat(string format, int i, out int argIndex, out int padding, out int precision, internal static int GetFormat(string format, int i, out int argIndex, out int padding, out int precision, out int alignment)
out int alignment)
{ {
argIndex = -1; argIndex = -1;
padding = 0; padding = 0;

View File

@ -1,35 +1,26 @@
using UnityEngine; using UnityEngine;
using UnityEngine.Serialization;
namespace Coffee.UIExtensions.Demo namespace Coffee.UIExtensions.Demo
{ {
public class UIParticle_PerformanceDemo : MonoBehaviour public class UIParticle_PerformanceDemo : MonoBehaviour
{ {
[FormerlySerializedAs("copyOrigin")] public GameObject copyOrigin;
[SerializeField] public int copyCount;
private GameObject m_CopyOrigin; public Canvas root;
[FormerlySerializedAs("copyCount")]
[SerializeField]
public int m_CopyCount;
[FormerlySerializedAs("root")]
[SerializeField]
public Canvas m_RootCanvas;
private void Start() private void Start()
{ {
Application.targetFrameRate = 60; Application.targetFrameRate = 60;
if (m_CopyOrigin) if (copyOrigin)
{ {
m_CopyOrigin.SetActive(false); copyOrigin.SetActive(false);
var parent = m_CopyOrigin.transform.parent; var parent = copyOrigin.transform.parent;
for (var i = 0; i < m_CopyCount; i++) for (var i = 0; i < copyCount; i++)
{ {
var go = Instantiate(m_CopyOrigin, parent, false); var go = GameObject.Instantiate(copyOrigin, parent, false);
go.name = string.Format("{0} {1}", m_CopyOrigin.name, i + 1); go.name = string.Format("{0} {1}", copyOrigin.name, i + 1);
go.hideFlags = HideFlags.DontSave; go.hideFlags = HideFlags.DontSave;
go.SetActive(true); go.SetActive(true);
@ -37,14 +28,32 @@ namespace Coffee.UIExtensions.Demo
} }
} }
public void UIParticle_Enable(bool flag) public void ChangeScreenSize()
{ {
foreach (var uip in m_RootCanvas.GetComponentsInChildren<UIParticle>(true)) if (Screen.width == 400 && Screen.height == 720)
{ Screen.SetResolution(720, 720, false);
uip.enabled = flag; else if (Screen.width == 720 && Screen.height == 720)
Screen.SetResolution(720, 400, false);
else
Screen.SetResolution(400, 720, false);
} }
if (!flag) public void EnableAnimations(bool enabled)
{
foreach (var animator in FindObjectsOfType<Animator>())
{
animator.enabled = enabled;
}
}
public void UIParticle_Enable(bool enabled)
{
foreach (var uip in root.GetComponentsInChildren<UIParticle>(true))
{
uip.enabled = enabled;
}
if (!enabled)
{ {
foreach (var ps in FindObjectsOfType<ParticleSystem>()) foreach (var ps in FindObjectsOfType<ParticleSystem>())
{ {
@ -53,23 +62,36 @@ namespace Coffee.UIExtensions.Demo
} }
} }
public void UIParticle_MeshSharing(bool flag) public void UIParticle_MeshSharing(bool enabled)
{ {
foreach (var uip in m_RootCanvas.GetComponentsInChildren<UIParticle>(true)) foreach (var uip in root.GetComponentsInChildren<UIParticle>(true))
{ {
uip.meshSharing = flag uip.meshSharing = enabled ? UIParticle.MeshSharing.Auto : UIParticle.MeshSharing.None;
? UIParticle.MeshSharing.Auto
: UIParticle.MeshSharing.None;
} }
} }
public void UIParticle_RandomGroup(bool flag) public void UIParticle_RandomGroup(bool enabled)
{ {
foreach (var uip in m_RootCanvas.GetComponentsInChildren<UIParticle>(true)) foreach (var uip in root.GetComponentsInChildren<UIParticle>(true))
{ {
uip.groupMaxId = flag uip.groupMaxId = enabled ? 4 : 0;
? 4 }
: 0; }
public void UIParticle_Scale(float scale)
{
foreach (var uip in FindObjectsOfType<UIParticle>())
{
uip.scale = scale;
}
}
public void ParticleSystem_WorldSpaseSimulation(bool enabled)
{
foreach (var ps in FindObjectsOfType<ParticleSystem>())
{
var main = ps.main;
main.simulationSpace = enabled ? ParticleSystemSimulationSpace.World : ParticleSystemSimulationSpace.Local;
} }
} }
@ -80,5 +102,34 @@ namespace Coffee.UIExtensions.Demo
ps.transform.localScale = new Vector3(scale, scale, scale); ps.transform.localScale = new Vector3(scale, scale, scale);
} }
} }
public void Canvas_WorldSpace(bool flag)
{
if (flag)
{
var canvas = FindObjectOfType<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceCamera;
canvas.renderMode = RenderMode.WorldSpace;
canvas.transform.rotation = Quaternion.Euler(new Vector3(0, 10, 0));
}
}
public void Canvas_CameraSpace(bool flag)
{
if (flag)
{
var canvas = FindObjectOfType<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceCamera;
}
}
public void Canvas_Overlay(bool flag)
{
if (flag)
{
var canvas = FindObjectOfType<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
}
}
} }
} }

View File

@ -38,7 +38,7 @@ RenderSettings:
m_ReflectionIntensity: 1 m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0} m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0} m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0.12731689, g: 0.13414702, b: 0.1210784, a: 1} m_IndirectSpecularColor: {r: 0.12731704, g: 0.13414726, b: 0.1210787, a: 1}
m_UseRadianceAmbientProbe: 0 m_UseRadianceAmbientProbe: 0
--- !u!157 &3 --- !u!157 &3
LightmapSettings: LightmapSettings:
@ -757,6 +757,85 @@ Transform:
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_RootOrder: 2 m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &455490117
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 455490120}
- component: {fileID: 455490119}
- component: {fileID: 455490118}
m_Layer: 5
m_Name: Custom 1
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &455490118
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 455490117}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b0e08c6080a2e4d8186f97c60518830f, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 12800000, guid: 70069a8db1df148ae8b4c8108c079acf, type: 3}
m_FontSize: 14
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 1
m_MaxSize: 40
m_Alignment: 0
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: 'UIParticle: 888'
m_Mode: 0
m_TextAnchor: 0
--- !u!222 &455490119
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 455490117}
m_CullTransparentMesh: 0
--- !u!224 &455490120
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 455490117}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 1002673088}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 251.8711, y: -10}
m_SizeDelta: {x: 247.8711, y: 20}
m_Pivot: {x: 0, y: 0.5}
--- !u!1 &496349153 --- !u!1 &496349153
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -893,12 +972,12 @@ PrefabInstance:
- target: {fileID: 864895766138689324, guid: a4669dfbba654429bbcddf0c9b79fb8b, - target: {fileID: 864895766138689324, guid: a4669dfbba654429bbcddf0c9b79fb8b,
type: 3} type: 3}
propertyPath: m_AnchoredPosition.x propertyPath: m_AnchoredPosition.x
value: 240 value: 240.00002
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 864895766138689324, guid: a4669dfbba654429bbcddf0c9b79fb8b, - target: {fileID: 864895766138689324, guid: a4669dfbba654429bbcddf0c9b79fb8b,
type: 3} type: 3}
propertyPath: m_AnchoredPosition.y propertyPath: m_AnchoredPosition.y
value: -180 value: -179.99998
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 864895766138689324, guid: a4669dfbba654429bbcddf0c9b79fb8b, - target: {fileID: 864895766138689324, guid: a4669dfbba654429bbcddf0c9b79fb8b,
type: 3} type: 3}
@ -1081,85 +1160,6 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 608849065} m_GameObject: {fileID: 608849065}
m_CullTransparentMesh: 0 m_CullTransparentMesh: 0
--- !u!1 &733597319
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 733597322}
- component: {fileID: 733597321}
- component: {fileID: 733597320}
m_Layer: 5
m_Name: Custom 1
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &733597320
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 733597319}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b0e08c6080a2e4d8186f97c60518830f, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 12800000, guid: d07e6c2670f164cf7939ab011061a9bf, type: 3}
m_FontSize: 14
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 1
m_MaxSize: 40
m_Alignment: 0
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: 'Screen: 8888x8888 Time: 8888.88'
m_Mode: 0
m_TextAnchor: 0
--- !u!222 &733597321
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 733597319}
m_CullTransparentMesh: 0
--- !u!224 &733597322
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 733597319}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 1002673088}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 251.8711, y: -10}
m_SizeDelta: {x: 247.8711, y: 20}
m_Pivot: {x: 0, y: 0.5}
--- !u!1 &879285143 --- !u!1 &879285143
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -1287,9 +1287,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 3a595cf9cf93f4e0686f247a5e7e19ce, type: 3} m_Script: {fileID: 11500000, guid: 3a595cf9cf93f4e0686f247a5e7e19ce, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
m_CopyOrigin: {fileID: 1581241706} copyOrigin: {fileID: 1581241706}
m_CopyCount: 266 copyCount: 266
m_RootCanvas: {fileID: 919637305} root: {fileID: 919637305}
--- !u!4 &903906985 --- !u!4 &903906985
Transform: Transform:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -1505,6 +1505,11 @@ PrefabInstance:
propertyPath: m_SizeDelta.x propertyPath: m_SizeDelta.x
value: 0 value: 0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 151878254403844079, guid: 784696794bc6345bc80bf49623581c2e,
type: 3}
propertyPath: m_TargetGraphic
value:
objectReference: {fileID: 1002673091}
- target: {fileID: 323826319202443003, guid: 784696794bc6345bc80bf49623581c2e, - target: {fileID: 323826319202443003, guid: 784696794bc6345bc80bf49623581c2e,
type: 3} type: 3}
propertyPath: m_AnchorMin.y propertyPath: m_AnchorMin.y
@ -1525,6 +1530,11 @@ PrefabInstance:
propertyPath: m_AnchoredPosition.y propertyPath: m_AnchoredPosition.y
value: 0 value: 0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 1087632561348207336, guid: 784696794bc6345bc80bf49623581c2e,
type: 3}
propertyPath: m_TargetGraphic
value:
objectReference: {fileID: 1002673092}
- target: {fileID: 1458366075964155596, guid: 784696794bc6345bc80bf49623581c2e, - target: {fileID: 1458366075964155596, guid: 784696794bc6345bc80bf49623581c2e,
type: 3} type: 3}
propertyPath: m_AnchorMin.y propertyPath: m_AnchorMin.y
@ -1554,7 +1564,7 @@ PrefabInstance:
type: 3} type: 3}
propertyPath: m_CustomMonitorItems.Array.data[1].m_Text propertyPath: m_CustomMonitorItems.Array.data[1].m_Text
value: value:
objectReference: {fileID: 733597320} objectReference: {fileID: 455490118}
- target: {fileID: 4567906826058368312, guid: 784696794bc6345bc80bf49623581c2e, - target: {fileID: 4567906826058368312, guid: 784696794bc6345bc80bf49623581c2e,
type: 3} type: 3}
propertyPath: m_CustomMonitorItems.Array.data[1].m_Format propertyPath: m_CustomMonitorItems.Array.data[1].m_Format
@ -1620,6 +1630,16 @@ PrefabInstance:
propertyPath: m_SizeDelta.x propertyPath: m_SizeDelta.x
value: 0 value: 0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 5526675317024915317, guid: 784696794bc6345bc80bf49623581c2e,
type: 3}
propertyPath: m_TargetGraphic
value:
objectReference: {fileID: 1002673089}
- target: {fileID: 5526675317129615258, guid: 784696794bc6345bc80bf49623581c2e,
type: 3}
propertyPath: m_TargetGraphic
value:
objectReference: {fileID: 1002673090}
- target: {fileID: 5758968653464739090, guid: 784696794bc6345bc80bf49623581c2e, - target: {fileID: 5758968653464739090, guid: 784696794bc6345bc80bf49623581c2e,
type: 3} type: 3}
propertyPath: m_SizeDelta.y propertyPath: m_SizeDelta.y
@ -1783,6 +1803,54 @@ RectTransform:
type: 3} type: 3}
m_PrefabInstance: {fileID: 1002673087} m_PrefabInstance: {fileID: 1002673087}
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
--- !u!114 &1002673089 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 5526675317024915314, guid: 784696794bc6345bc80bf49623581c2e,
type: 3}
m_PrefabInstance: {fileID: 1002673087}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b0e08c6080a2e4d8186f97c60518830f, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &1002673090 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 5526675317129615259, guid: 784696794bc6345bc80bf49623581c2e,
type: 3}
m_PrefabInstance: {fileID: 1002673087}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b0e08c6080a2e4d8186f97c60518830f, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &1002673091 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 6638879077379268485, guid: 784696794bc6345bc80bf49623581c2e,
type: 3}
m_PrefabInstance: {fileID: 1002673087}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b0e08c6080a2e4d8186f97c60518830f, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &1002673092 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 6720252555115459274, guid: 784696794bc6345bc80bf49623581c2e,
type: 3}
m_PrefabInstance: {fileID: 1002673087}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b0e08c6080a2e4d8186f97c60518830f, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &1023851109 --- !u!1 &1023851109
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -3564,11 +3632,6 @@ PrefabInstance:
m_Modification: m_Modification:
m_TransformParent: {fileID: 1924051224} m_TransformParent: {fileID: 1924051224}
m_Modifications: m_Modifications:
- target: {fileID: 864895764861262036, guid: a4669dfbba654429bbcddf0c9b79fb8b,
type: 3}
propertyPath: m_IgnoreCanvasScaler
value: 1
objectReference: {fileID: 0}
- target: {fileID: 864895765507486433, guid: a4669dfbba654429bbcddf0c9b79fb8b, - target: {fileID: 864895765507486433, guid: a4669dfbba654429bbcddf0c9b79fb8b,
type: 3} type: 3}
propertyPath: m_AnchorMax.x propertyPath: m_AnchorMax.x
@ -3727,17 +3790,17 @@ PrefabInstance:
- target: {fileID: 1088568576793901561, guid: a4669dfbba654429bbcddf0c9b79fb8b, - target: {fileID: 1088568576793901561, guid: a4669dfbba654429bbcddf0c9b79fb8b,
type: 3} type: 3}
propertyPath: m_LocalScale.x propertyPath: m_LocalScale.x
value: 1 value: 0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 1088568576793901561, guid: a4669dfbba654429bbcddf0c9b79fb8b, - target: {fileID: 1088568576793901561, guid: a4669dfbba654429bbcddf0c9b79fb8b,
type: 3} type: 3}
propertyPath: m_LocalScale.y propertyPath: m_LocalScale.y
value: 1 value: 0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 1088568576793901561, guid: a4669dfbba654429bbcddf0c9b79fb8b, - target: {fileID: 1088568576793901561, guid: a4669dfbba654429bbcddf0c9b79fb8b,
type: 3} type: 3}
propertyPath: m_LocalScale.z propertyPath: m_LocalScale.z
value: 1 value: 0
objectReference: {fileID: 0} objectReference: {fileID: 0}
m_RemovedComponents: [] m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: a4669dfbba654429bbcddf0c9b79fb8b, type: 3} m_SourcePrefab: {fileID: 100100000, guid: a4669dfbba654429bbcddf0c9b79fb8b, type: 3}

View File

@ -1,9 +1,8 @@
using System; using UnityEngine;
using UnityEngine;
namespace Coffee.UIExtensions namespace Coffee.UIExtensions
{ {
[Serializable] [System.Serializable]
public class AnimatableProperty : ISerializationCallbackReceiver public class AnimatableProperty : ISerializationCallbackReceiver
{ {
public enum ShaderPropertyType public enum ShaderPropertyType
@ -12,11 +11,11 @@ namespace Coffee.UIExtensions
Vector, Vector,
Float, Float,
Range, Range,
Texture Texture,
} }
[SerializeField] private string m_Name = ""; [SerializeField] string m_Name = "";
[SerializeField] private ShaderPropertyType m_Type = ShaderPropertyType.Vector; [SerializeField] ShaderPropertyType m_Type = ShaderPropertyType.Vector;
public int id { get; private set; } public int id { get; private set; }
public ShaderPropertyType type public ShaderPropertyType type
@ -24,15 +23,6 @@ namespace Coffee.UIExtensions
get { return m_Type; } get { return m_Type; }
} }
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
}
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
id = Shader.PropertyToID(m_Name);
}
public void UpdateMaterialProperties(Material material, MaterialPropertyBlock mpb) public void UpdateMaterialProperties(Material material, MaterialPropertyBlock mpb)
{ {
if (!material.HasProperty(id)) return; if (!material.HasProperty(id)) return;
@ -41,38 +31,35 @@ namespace Coffee.UIExtensions
{ {
case ShaderPropertyType.Color: case ShaderPropertyType.Color:
var color = mpb.GetColor(id); var color = mpb.GetColor(id);
if (color != default) if (color != default(Color))
{
material.SetColor(id, color); material.SetColor(id, color);
}
break; break;
case ShaderPropertyType.Vector: case ShaderPropertyType.Vector:
var vector = mpb.GetVector(id); var vector = mpb.GetVector(id);
if (vector != default) if (vector != default(Vector4))
{
material.SetVector(id, vector); material.SetVector(id, vector);
}
break; break;
case ShaderPropertyType.Float: case ShaderPropertyType.Float:
case ShaderPropertyType.Range: case ShaderPropertyType.Range:
var value = mpb.GetFloat(id); var value = mpb.GetFloat(id);
if (!Mathf.Approximately(value, 0)) if (value != default(float))
{
material.SetFloat(id, value); material.SetFloat(id, value);
}
break; break;
case ShaderPropertyType.Texture: case ShaderPropertyType.Texture:
var tex = mpb.GetTexture(id); var tex = mpb.GetTexture(id);
if (tex != default(Texture)) if (tex != default(Texture))
{
material.SetTexture(id, tex); material.SetTexture(id, tex);
}
break; break;
} }
} }
public void OnBeforeSerialize()
{
}
public void OnAfterDeserialize()
{
id = Shader.PropertyToID(m_Name);
}
} }
} }

View File

@ -1,143 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
namespace Coffee.UIExtensions
{
internal static class AnimatablePropertyEditor
{
private static readonly GUIContent s_ContentNothing = new GUIContent("Nothing");
private static readonly List<string> s_ActiveNames = new List<string>();
private static readonly StringBuilder s_Sb = new StringBuilder();
private static readonly HashSet<string> s_Names = new HashSet<string>();
private static string CollectActiveNames(SerializedProperty sp, List<string> result)
{
result.Clear();
for (var i = 0; i < sp.arraySize; i++)
{
var spName = sp.GetArrayElementAtIndex(i).FindPropertyRelative("m_Name");
if (spName == null) continue;
result.Add(spName.stringValue);
}
s_Sb.Length = 0;
if (result.Count == 0)
{
s_Sb.Append("Nothing");
}
else
{
result.Aggregate(s_Sb, (a, b) => s_Sb.AppendFormat("{0}, ", b));
s_Sb.Length -= 2;
}
return s_Sb.ToString();
}
public static void Draw(SerializedProperty sp, Material[] mats)
{
bool isClicked;
using (new EditorGUILayout.HorizontalScope(GUILayout.ExpandWidth(false)))
{
var pos = EditorGUILayout.GetControlRect(true);
var label = new GUIContent(sp.displayName, sp.tooltip);
var rect = EditorGUI.PrefixLabel(pos, label);
var text = sp.hasMultipleDifferentValues
? "-"
: CollectActiveNames(sp, s_ActiveNames);
isClicked = GUI.Button(rect, text, EditorStyles.popup);
}
if (!isClicked) return;
var gm = new GenericMenu();
gm.AddItem(s_ContentNothing, s_ActiveNames.Count == 0, () =>
{
sp.ClearArray();
sp.serializedObject.ApplyModifiedProperties();
});
if (!sp.hasMultipleDifferentValues)
{
for (var i = 0; i < sp.arraySize; i++)
{
var p = sp.GetArrayElementAtIndex(i);
var name = p.FindPropertyRelative("m_Name").stringValue;
var type = (AnimatableProperty.ShaderPropertyType)p.FindPropertyRelative("m_Type").intValue;
AddMenu(gm, sp, new ShaderProperty(name, type), false);
}
}
s_Names.Clear();
for (var j = 0; j < mats.Length; j++)
{
var mat = mats[j];
if (!mat || !mat.shader) continue;
for (var i = 0; i < ShaderUtil.GetPropertyCount(mat.shader); i++)
{
var name = ShaderUtil.GetPropertyName(mat.shader, i);
var type = (AnimatableProperty.ShaderPropertyType)ShaderUtil.GetPropertyType(mat.shader, i);
if (s_Names.Contains(name)) continue;
s_Names.Add(name);
AddMenu(gm, sp, new ShaderProperty(name, type), true);
if (type != AnimatableProperty.ShaderPropertyType.Texture) continue;
AddMenu(gm, sp, new ShaderProperty($"{name}_ST"), true);
AddMenu(gm, sp, new ShaderProperty($"{name}_HDR"), true);
AddMenu(gm, sp, new ShaderProperty($"{name}_TexelSize"), true);
}
}
gm.ShowAsContext();
}
private static void AddMenu(GenericMenu menu, SerializedProperty sp, ShaderProperty prop, bool add)
{
if (add && s_ActiveNames.Contains(prop.name)) return;
var label = new GUIContent($"{prop.name} ({prop.type})");
menu.AddItem(label, s_ActiveNames.Contains(prop.name), () =>
{
var index = s_ActiveNames.IndexOf(prop.name);
if (0 <= index)
{
sp.DeleteArrayElementAtIndex(index);
}
else
{
sp.InsertArrayElementAtIndex(sp.arraySize);
var p = sp.GetArrayElementAtIndex(sp.arraySize - 1);
p.FindPropertyRelative("m_Name").stringValue = prop.name;
p.FindPropertyRelative("m_Type").intValue = (int)prop.type;
}
sp.serializedObject.ApplyModifiedProperties();
});
}
private struct ShaderProperty
{
public readonly string name;
public readonly AnimatableProperty.ShaderPropertyType type;
public ShaderProperty(string name)
{
this.name = name;
type = AnimatableProperty.ShaderPropertyType.Vector;
}
public ShaderProperty(string name, AnimatableProperty.ShaderPropertyType type)
{
this.name = name;
this.type = type;
}
}
}
}

View File

@ -0,0 +1,123 @@
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using ShaderPropertyType = Coffee.UIExtensions.AnimatableProperty.ShaderPropertyType;
namespace Coffee.UIExtensions
{
internal class AnimatedPropertiesEditor
{
private static readonly List<string> s_ActiveNames = new List<string>();
private static readonly System.Text.StringBuilder s_Sb = new System.Text.StringBuilder();
private static readonly HashSet<string> s_Names = new HashSet<string>();
private string _name;
private ShaderPropertyType _type;
static string CollectActiveNames(SerializedProperty sp, List<string> result)
{
result.Clear();
for (var i = 0; i < sp.arraySize; i++)
{
var spName = sp.GetArrayElementAtIndex(i).FindPropertyRelative("m_Name");
if (spName == null) continue;
result.Add(spName.stringValue);
}
s_Sb.Length = 0;
if (result.Count == 0)
{
s_Sb.Append("Nothing");
}
else
{
result.Aggregate(s_Sb, (a, b) => s_Sb.AppendFormat("{0}, ", b));
s_Sb.Length -= 2;
}
return s_Sb.ToString();
}
public static void DrawAnimatableProperties(SerializedProperty sp, Material[] mats)
{
bool isClicked;
using (new EditorGUILayout.HorizontalScope(GUILayout.ExpandWidth(false)))
{
var r = EditorGUI.PrefixLabel(EditorGUILayout.GetControlRect(true), new GUIContent(sp.displayName, sp.tooltip));
var text = sp.hasMultipleDifferentValues ? "-" : CollectActiveNames(sp, s_ActiveNames);
isClicked = GUI.Button(r, text, EditorStyles.popup);
}
if (!isClicked) return;
var gm = new GenericMenu();
gm.AddItem(new GUIContent("Nothing"), s_ActiveNames.Count == 0, () =>
{
sp.ClearArray();
sp.serializedObject.ApplyModifiedProperties();
});
if (!sp.hasMultipleDifferentValues)
{
for (var i = 0; i < sp.arraySize; i++)
{
var p = sp.GetArrayElementAtIndex(i);
var name = p.FindPropertyRelative("m_Name").stringValue;
var type = (ShaderPropertyType) p.FindPropertyRelative("m_Type").intValue;
AddMenu(gm, sp, new AnimatedPropertiesEditor {_name = name, _type = type}, false);
}
}
s_Names.Clear();
foreach (var mat in mats)
{
if (!mat || !mat.shader) continue;
for (var i = 0; i < ShaderUtil.GetPropertyCount(mat.shader); i++)
{
var pName = ShaderUtil.GetPropertyName(mat.shader, i);
var type = (ShaderPropertyType) ShaderUtil.GetPropertyType(mat.shader, i);
var name = string.Format("{0} ({1})", pName, type);
if (s_Names.Contains(name)) continue;
s_Names.Add(name);
AddMenu(gm, sp, new AnimatedPropertiesEditor {_name = pName, _type = type}, true);
if (type != ShaderPropertyType.Texture) continue;
AddMenu(gm, sp, new AnimatedPropertiesEditor {_name = pName + "_ST", _type = ShaderPropertyType.Vector}, true);
AddMenu(gm, sp, new AnimatedPropertiesEditor {_name = pName + "_HDR", _type = ShaderPropertyType.Vector}, true);
AddMenu(gm, sp, new AnimatedPropertiesEditor {_name = pName + "_TexelSize", _type = ShaderPropertyType.Vector}, true);
}
}
gm.ShowAsContext();
}
private static void AddMenu(GenericMenu menu, SerializedProperty sp, AnimatedPropertiesEditor property, bool add)
{
if (add && s_ActiveNames.Contains(property._name)) return;
menu.AddItem(new GUIContent(string.Format("{0} ({1})", property._name, property._type)), s_ActiveNames.Contains(property._name), () =>
{
var index = s_ActiveNames.IndexOf(property._name);
if (0 <= index)
{
sp.DeleteArrayElementAtIndex(index);
}
else
{
sp.InsertArrayElementAtIndex(sp.arraySize);
var p = sp.GetArrayElementAtIndex(sp.arraySize - 1);
p.FindPropertyRelative("m_Name").stringValue = property._name;
p.FindPropertyRelative("m_Type").intValue = (int) property._type;
}
sp.serializedObject.ApplyModifiedProperties();
});
}
}
}

View File

@ -32,16 +32,11 @@ namespace Coffee.UIExtensions
private static void ImportSample(string jsonGuid, string sampleName) private static void ImportSample(string jsonGuid, string sampleName)
{ {
var jsonPath = AssetDatabase.GUIDToAssetPath(jsonGuid); var jsonPath = AssetDatabase.GUIDToAssetPath(jsonGuid);
if (string.IsNullOrEmpty(jsonPath)) return; var packageRoot = Path.GetDirectoryName(jsonPath).Replace('\\', '/');
var jsonDir = Path.GetDirectoryName(jsonPath);
if (string.IsNullOrEmpty(jsonDir)) return;
var packageRoot = jsonDir.Replace('\\', '/');
var json = File.ReadAllText(jsonPath); var json = File.ReadAllText(jsonPath);
var version = Regex.Match(json, "\"version\"\\s*:\\s*\"([^\"]+)\"").Groups[1].Value; var version = Regex.Match(json, "\"version\"\\s*:\\s*\"([^\"]+)\"").Groups[1].Value;
var src = $"{packageRoot}/Samples~/{sampleName}"; var src = string.Format("{0}/Samples~/{1}", packageRoot, sampleName);
var dst = $"Assets/Samples/{k_DisplayName}/{version}/{sampleName}"; var dst = string.Format("Assets/Samples/{0}/{1}/{2}", k_DisplayName, version, sampleName);
var previousPath = GetPreviousSamplePath(k_DisplayName, sampleName); var previousPath = GetPreviousSamplePath(k_DisplayName, sampleName);
// Remove the previous sample directory. // Remove the previous sample directory.
@ -51,45 +46,33 @@ namespace Coffee.UIExtensions
+ previousPath + previousPath
+ "\n\nIt will be deleted when you update. Are you sure you want to continue?"; + "\n\nIt will be deleted when you update. Are you sure you want to continue?";
if (!EditorUtility.DisplayDialog("Sample Importer", msg, "OK", "Cancel")) if (!EditorUtility.DisplayDialog("Sample Importer", msg, "OK", "Cancel"))
{
return; return;
}
FileUtil.DeleteFileOrDirectory(previousPath); FileUtil.DeleteFileOrDirectory(previousPath);
var metaFile = previousPath + ".meta"; var metaFile = previousPath + ".meta";
if (File.Exists(metaFile)) if (File.Exists(metaFile))
{
FileUtil.DeleteFileOrDirectory(metaFile); FileUtil.DeleteFileOrDirectory(metaFile);
} }
}
if (!Directory.Exists(dst)) if (!Directory.Exists(dst))
{
FileUtil.DeleteFileOrDirectory(dst); FileUtil.DeleteFileOrDirectory(dst);
}
var dstDir = Path.GetDirectoryName(dst); var dstDir = Path.GetDirectoryName(dst);
if (!string.IsNullOrEmpty(dstDir) && !Directory.Exists(dstDir)) if (!Directory.Exists(dstDir))
{
Directory.CreateDirectory(dstDir); Directory.CreateDirectory(dstDir);
}
if (Directory.Exists(src)) if (Directory.Exists(src))
{
FileUtil.CopyFileOrDirectory(src, dst); FileUtil.CopyFileOrDirectory(src, dst);
}
else else
{
throw new DirectoryNotFoundException(src); throw new DirectoryNotFoundException(src);
}
AssetDatabase.Refresh(ImportAssetOptions.ImportRecursive); AssetDatabase.Refresh(ImportAssetOptions.ImportRecursive);
} }
private static string GetPreviousSamplePath(string displayName, string sampleName) private static string GetPreviousSamplePath(string displayName, string sampleName)
{ {
var sampleRoot = $"Assets/Samples/{displayName}"; var sampleRoot = string.Format("Assets/Samples/{0}", displayName);
var sampleRootInfo = new DirectoryInfo(sampleRoot); var sampleRootInfo = new DirectoryInfo(sampleRoot);
if (!sampleRootInfo.Exists) return null; if (!sampleRootInfo.Exists) return null;

View File

@ -1,24 +1,19 @@
#if UNITY_2019_3_11 || UNITY_2019_3_12 || UNITY_2019_3_13 || UNITY_2019_3_14 || UNITY_2019_3_15 || UNITY_2019_4_OR_NEWER
#define SERIALIZE_FIELD_MASKABLE
#endif
using UnityEditor;
using UnityEditor.UI;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using UnityEditorInternal;
using UnityEngine.UI;
using System;
#if UNITY_2021_2_OR_NEWER #if UNITY_2021_2_OR_NEWER
using UnityEditor.Overlays; using UnityEditor.Overlays;
#else #else
using System.Reflection; using System.Reflection;
#endif #endif
#if UNITY_2021_2_OR_NEWER
using UnityEditor.SceneManagement;
#elif UNITY_2018_3_OR_NEWER
using UnityEditor.Experimental.SceneManagement;
#endif
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Coffee.UIParticleExtensions;
using UnityEditor;
using UnityEditor.UI;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.UI;
using Object = UnityEngine.Object;
namespace Coffee.UIExtensions namespace Coffee.UIExtensions
{ {
@ -28,10 +23,7 @@ namespace Coffee.UIExtensions
{ {
#if UNITY_2021_2_OR_NEWER #if UNITY_2021_2_OR_NEWER
#if UNITY_2022_1_OR_NEWER #if UNITY_2022_1_OR_NEWER
[Overlay(typeof(SceneView), "Scene View/UI Particles", "UI Particles", true, [Overlay(typeof(SceneView), "Scene View/UI Particles", "UI Particles", true, defaultDockPosition = DockPosition.Bottom, defaultDockZone = DockZone.Floating, defaultLayout = Layout.Panel)]
defaultDockPosition = DockPosition.Bottom,
defaultDockZone = DockZone.Floating,
defaultLayout = Layout.Panel)]
#else #else
[Overlay(typeof(SceneView), "Scene View/UI Particles", "UI Particles", true)] [Overlay(typeof(SceneView), "Scene View/UI Particles", "UI Particles", true)]
#endif #endif
@ -61,22 +53,24 @@ namespace Coffee.UIExtensions
private static readonly GUIContent s_ContentRandom = new GUIContent("Random"); private static readonly GUIContent s_ContentRandom = new GUIContent("Random");
private static readonly GUIContent s_ContentScale = new GUIContent("Scale"); private static readonly GUIContent s_ContentScale = new GUIContent("Scale");
private static SerializedObject s_SerializedObject; private static SerializedObject s_SerializedObject;
private static bool s_XYZMode;
private SerializedProperty _maskable; #if !SERIALIZE_FIELD_MASKABLE
private SerializedProperty _scale3D; private SerializedProperty m_Maskable;
private SerializedProperty _animatableProperties; #endif
private SerializedProperty _meshSharing; private SerializedProperty m_Scale3D;
private SerializedProperty _groupId; private SerializedProperty m_AnimatableProperties;
private SerializedProperty _groupMaxId; private SerializedProperty m_MeshSharing;
private SerializedProperty _positionMode; private SerializedProperty m_GroupId;
private SerializedProperty _autoScaling; private SerializedProperty m_GroupMaxId;
private SerializedProperty m_AbsoluteMode;
private ReorderableList _ro; private ReorderableList _ro;
static private bool _xyzMode;
private bool _showMax; private bool _showMax;
private static readonly HashSet<Shader> s_Shaders = new HashSet<Shader>(); private static readonly HashSet<Shader> s_Shaders = new HashSet<Shader>();
private static readonly List<ParticleSystemVertexStream> s_Streams = new List<ParticleSystemVertexStream>(); private static readonly List<ParticleSystemVertexStream> s_Streams = new List<ParticleSystemVertexStream>();
private static readonly List<string> s_MaskablePropertyNames = new List<string> private static readonly List<string> s_MaskablePropertyNames = new List<string>
{ {
"_Stencil", "_Stencil",
@ -84,20 +78,21 @@ namespace Coffee.UIExtensions
"_StencilOp", "_StencilOp",
"_StencilWriteMask", "_StencilWriteMask",
"_StencilReadMask", "_StencilReadMask",
"_ColorMask" "_ColorMask",
}; };
[InitializeOnLoadMethod] [InitializeOnLoadMethod]
private static void Init() static void Init()
{ {
#if !UNITY_2021_2_OR_NEWER #if !UNITY_2021_2_OR_NEWER
var miSceneViewOverlayWindow = Type.GetType("UnityEditor.SceneViewOverlay, UnityEditor") var miSceneViewOverlayWindow = Type.GetType("UnityEditor.SceneViewOverlay, UnityEditor")
?.GetMethods(BindingFlags.Public | BindingFlags.Static) .GetMethods(BindingFlags.Public | BindingFlags.Static)
.First(x => x.Name == "Window" && 5 <= x.GetParameters().Length); .First(x => x.Name == "Window" && 5 <= x.GetParameters().Length);
var windowFunction = (Action<Object, SceneView>)WindowFunction; var windowFunction = (Action<UnityEngine.Object, SceneView>)WindowFunction;
var windowFunctionType = Type.GetType("UnityEditor.SceneViewOverlay+WindowFunction, UnityEditor"); var windowFunctionType = Type.GetType("UnityEditor.SceneViewOverlay+WindowFunction, UnityEditor");
var windowFunctionDelegate = Delegate.CreateDelegate(windowFunctionType, windowFunction.Method); var windowFunctionDelegate = Delegate.CreateDelegate(windowFunctionType, windowFunction.Method);
var windowTitle = new GUIContent(ObjectNames.NicifyVariableName(nameof(UIParticle))); var windowTitle = new GUIContent(ObjectNames.NicifyVariableName(typeof(UIParticle).Name));
#if UNITY_2019_2_OR_NEWER #if UNITY_2019_2_OR_NEWER
//public static void Window(GUIContent title, WindowFunction sceneViewFunc, int order, Object target, WindowDisplayOption option, EditorWindow window = null) //public static void Window(GUIContent title, WindowFunction sceneViewFunc, int order, Object target, WindowDisplayOption option, EditorWindow window = null)
var sceneViewArgs = new object[] { windowTitle, windowFunctionDelegate, 599, null, 2, null }; var sceneViewArgs = new object[] { windowTitle, windowFunctionDelegate, 599, null, 2, null };
@ -107,7 +102,7 @@ namespace Coffee.UIExtensions
#endif #endif
#if UNITY_2019_1_OR_NEWER #if UNITY_2019_1_OR_NEWER
SceneView.duringSceneGui += _ => SceneView.duringSceneGui += _ => { if (s_SerializedObject != null) miSceneViewOverlayWindow.Invoke(null, sceneViewArgs); };
#else #else
SceneView.onSceneGUIDelegate += _ => SceneView.onSceneGUIDelegate += _ =>
#endif #endif
@ -119,21 +114,25 @@ namespace Coffee.UIExtensions
}; };
#endif #endif
SerializedObject CreateSerializeObject() Func<SerializedObject> createSerializeObject = () =>
{ {
var uiParticles = Selection.gameObjects.Select(x => x.GetComponent<ParticleSystem>()) var uiParticles = Selection.gameObjects
.Select(x => x.GetComponent<ParticleSystem>())
.Where(x => x) .Where(x => x)
.Select(x => x.GetComponentInParent<UIParticle>(true)) .Select(x => x.GetComponentInParent<UIParticle>())
.Where(x => x && x.canvas) .Where(x => x && x.canvas)
.Concat(Selection.gameObjects.Select(x => x.GetComponent<UIParticle>()) .Concat(
.Where(x => x && x.canvas)) Selection.gameObjects
.Select(x => x.GetComponent<UIParticle>())
.Where(x => x && x.canvas)
)
.Distinct() .Distinct()
.ToArray(); .ToArray();
return 0 < uiParticles.Length ? new SerializedObject(uiParticles) : null; return 0 < uiParticles.Length ? new SerializedObject(uiParticles) : null;
} };
s_SerializedObject = CreateSerializeObject(); s_SerializedObject = createSerializeObject();
Selection.selectionChanged += () => s_SerializedObject = CreateSerializeObject(); Selection.selectionChanged += () => s_SerializedObject = createSerializeObject();
} }
//################################ //################################
@ -146,21 +145,19 @@ namespace Coffee.UIExtensions
{ {
base.OnEnable(); base.OnEnable();
_maskable = serializedObject.FindProperty("m_Maskable"); m_Maskable = serializedObject.FindProperty("m_Maskable");
_scale3D = serializedObject.FindProperty("m_Scale3D"); m_Scale3D = serializedObject.FindProperty("m_Scale3D");
_animatableProperties = serializedObject.FindProperty("m_AnimatableProperties"); m_AnimatableProperties = serializedObject.FindProperty("m_AnimatableProperties");
_meshSharing = serializedObject.FindProperty("m_MeshSharing"); m_MeshSharing = serializedObject.FindProperty("m_MeshSharing");
_groupId = serializedObject.FindProperty("m_GroupId"); m_GroupId = serializedObject.FindProperty("m_GroupId");
_groupMaxId = serializedObject.FindProperty("m_GroupMaxId"); m_GroupMaxId = serializedObject.FindProperty("m_GroupMaxId");
_positionMode = serializedObject.FindProperty("m_PositionMode"); m_AbsoluteMode = serializedObject.FindProperty("m_AbsoluteMode");
_autoScaling = serializedObject.FindProperty("m_AutoScaling");
var sp = serializedObject.FindProperty("m_Particles"); var sp = serializedObject.FindProperty("m_Particles");
_ro = new ReorderableList(sp.serializedObject, sp, true, true, true, true) _ro = new ReorderableList(sp.serializedObject, sp, true, true, true, true);
{ _ro.elementHeight = (EditorGUIUtility.singleLineHeight * 3) + 4;
elementHeight = EditorGUIUtility.singleLineHeight * 3 + 4, _ro.elementHeightCallback = _ => 3 * (EditorGUIUtility.singleLineHeight + 2);
elementHeightCallback = _ => 3 * (EditorGUIUtility.singleLineHeight + 2), _ro.drawElementCallback = (rect, index, _, __) =>
drawElementCallback = (rect, index, _, __) =>
{ {
EditorGUI.BeginDisabledGroup(sp.hasMultipleDifferentValues); EditorGUI.BeginDisabledGroup(sp.hasMultipleDifferentValues);
rect.y += 1; rect.y += 1;
@ -182,35 +179,29 @@ namespace Coffee.UIExtensions
{ {
materials.serializedObject.ApplyModifiedProperties(); materials.serializedObject.ApplyModifiedProperties();
} }
}, };
drawHeaderCallback = rect => _ro.drawHeaderCallback = rect =>
{ {
#if !UNITY_2019_3_OR_NEWER #if !UNITY_2019_3_OR_NEWER
rect.y -= 1; rect.y -= 1;
#endif #endif
var pos = new Rect(rect.x, rect.y, 150, rect.height); EditorGUI.LabelField(new Rect(rect.x, rect.y, 150, rect.height), s_ContentRenderingOrder);
EditorGUI.LabelField(pos, s_ContentRenderingOrder);
pos = new Rect(rect.width - 35, rect.y, 60, rect.height); if (GUI.Button(new Rect(rect.width - 35, rect.y, 60, rect.height), s_ContentRefresh, EditorStyles.miniButton))
if (GUI.Button(pos, s_ContentRefresh, EditorStyles.miniButton))
{ {
foreach (var uip in targets.OfType<UIParticle>()) foreach (UIParticle t in targets)
{ {
uip.RefreshParticles(); t.RefreshParticles();
EditorUtility.SetDirty(uip); EditorUtility.SetDirty(t);
}
} }
} }
}; };
// On select UIParticle, refresh particles. // On select UIParticle, refresh particles.
if (!Application.isPlaying) foreach (UIParticle t in targets)
{ {
foreach (var uip in targets.OfType<UIParticle>()) if (Application.isPlaying || PrefabUtility.GetPrefabAssetType(t) != PrefabAssetType.NotAPrefab) continue;
{ t.RefreshParticles(t.particles);
if (PrefabUtility.GetPrefabAssetType(uip) != PrefabAssetType.NotAPrefab) continue;
uip.RefreshParticles(uip.particles);
}
} }
} }
@ -239,11 +230,11 @@ namespace Coffee.UIExtensions
serializedObject.Update(); serializedObject.Update();
// Maskable // Maskable
EditorGUILayout.PropertyField(_maskable); EditorGUILayout.PropertyField(m_Maskable);
// Scale // Scale
EditorGUI.BeginDisabledGroup(!_meshSharing.hasMultipleDifferentValues && _meshSharing.intValue == 4); EditorGUI.BeginDisabledGroup(!m_MeshSharing.hasMultipleDifferentValues && m_MeshSharing.intValue == 4);
s_XYZMode = DrawFloatOrVector3Field(_scale3D, s_XYZMode); _xyzMode = DrawFloatOrVector3Field(m_Scale3D, _xyzMode);
EditorGUI.EndDisabledGroup(); EditorGUI.EndDisabledGroup();
// AnimatableProperties // AnimatableProperties
@ -253,11 +244,12 @@ namespace Coffee.UIExtensions
.Where(x => x) .Where(x => x)
.ToArray(); .ToArray();
AnimatablePropertyEditor.Draw(_animatableProperties, mats); // Animated properties
AnimatedPropertiesEditor.DrawAnimatableProperties(m_AnimatableProperties, mats);
// Mesh sharing // Mesh sharing
EditorGUI.BeginChangeCheck(); EditorGUI.BeginChangeCheck();
_showMax = DrawMeshSharing(_meshSharing, _groupId, _groupMaxId, _showMax); _showMax = DrawMeshSharing(m_MeshSharing, m_GroupId, m_GroupMaxId, _showMax);
if (EditorGUI.EndChangeCheck()) if (EditorGUI.EndChangeCheck())
{ {
serializedObject.ApplyModifiedProperties(); serializedObject.ApplyModifiedProperties();
@ -267,11 +259,8 @@ namespace Coffee.UIExtensions
} }
} }
// Position Mode // Absolute Mode
EditorGUILayout.PropertyField(_positionMode); EditorGUILayout.PropertyField(m_AbsoluteMode);
// Auto Scaling
DrawAutoScaling(_autoScaling, targets.OfType<UIParticle>());
// Target ParticleSystems. // Target ParticleSystems.
EditorGUI.BeginChangeCheck(); EditorGUI.BeginChangeCheck();
@ -287,22 +276,8 @@ namespace Coffee.UIExtensions
} }
} }
// Non-UI built-in shader is not supported.
foreach (var mat in current.materials)
{
if (!mat || !mat.shader) continue;
var shader = mat.shader;
if (IsBuiltInObject(shader) && !shader.name.StartsWith("UI/"))
{
EditorGUILayout.HelpBox(
$"Built-in shader '{shader.name}' in '{mat.name}' is not supported.\n" +
"Use UI shaders instead.",
MessageType.Error);
}
}
// Does the shader support UI masks? // Does the shader support UI masks?
if (current.maskable && current.GetComponentInParent<Mask>(false)) if (current.maskable && current.GetComponentInParent<Mask>())
{ {
foreach (var mat in current.materials) foreach (var mat in current.materials)
{ {
@ -314,20 +289,15 @@ namespace Coffee.UIExtensions
{ {
if (mat.HasProperty(propName)) continue; if (mat.HasProperty(propName)) continue;
EditorGUILayout.HelpBox( EditorGUILayout.HelpBox(string.Format("Shader '{0}' doesn't have '{1}' property. This graphic cannot be masked.", shader.name, propName), MessageType.Warning);
$"Shader '{shader.name}' doesn't have '{propName}' property." +
"\nThis graphic cannot be masked.",
MessageType.Warning);
break; break;
} }
} }
} }
s_Shaders.Clear(); s_Shaders.Clear();
// UIParticle for trail should be removed. // UIParticle for trail should be removed.
var label = "This UIParticle component should be removed. The UIParticle for trails is no longer needed."; if (FixButton(current.m_IsTrail, "This UIParticle component should be removed. The UIParticle for trails is no longer needed."))
if (FixButton(current.m_IsTrail, label))
{ {
DestroyUIParticle(current); DestroyUIParticle(current);
} }
@ -343,9 +313,7 @@ namespace Coffee.UIExtensions
{ {
var so = new SerializedObject(allPsRenderers); var so = new SerializedObject(allPsRenderers);
var sp = so.FindProperty("m_ApplyActiveColorSpace"); var sp = so.FindProperty("m_ApplyActiveColorSpace");
label = "When using linear color space, the particle colors are not output correctly.\n" + if (FixButton(sp.boolValue || sp.hasMultipleDifferentValues, "When using linear color space, the particle colors are not output correctly.\nTo fix, set 'Apply Active Color Space' in renderer module to false."))
"To fix, set 'Apply Active Color Space' in renderer module to false.";
if (FixButton(sp.boolValue || sp.hasMultipleDifferentValues, label))
{ {
sp.boolValue = false; sp.boolValue = false;
so.ApplyModifiedProperties(); so.ApplyModifiedProperties();
@ -360,21 +328,10 @@ namespace Coffee.UIExtensions
if (2 < s_Streams.Select(GetUsedComponentCount).Sum()) if (2 < s_Streams.Select(GetUsedComponentCount).Sum())
{ {
EditorGUILayout.HelpBox( EditorGUILayout.HelpBox(string.Format("ParticleSystem '{0}' uses 'TEXCOORD*.zw' components as custom vertex stream.\nUIParticle does not support it (See README.md).", psr.name), MessageType.Warning);
$"ParticleSystem '{psr.name}' uses 'TEXCOORD*.zw' components as custom vertex stream.\n" +
"UIParticle does not support it (See README.md).",
MessageType.Warning);
}
s_Streams.Clear();
} }
} }
} }
private bool IsBuiltInObject(Object obj)
{
return AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj, out var guid, out long _)
&& Regex.IsMatch(guid, "^0{16}.0{15}$", RegexOptions.Compiled);
} }
private static int GetUsedComponentCount(ParticleSystemVertexStream s) private static int GetUsedComponentCount(ParticleSystemVertexStream s)
@ -432,12 +389,10 @@ namespace Coffee.UIExtensions
case ParticleSystemVertexStream.Custom2XYZW: case ParticleSystemVertexStream.Custom2XYZW:
return 4; return 4;
} }
return 3; return 3;
} }
private static bool DrawMeshSharing(SerializedProperty spMeshSharing, SerializedProperty spGroupId, private static bool DrawMeshSharing(SerializedProperty spMeshSharing, SerializedProperty spGroupId, SerializedProperty spGroupMaxId, bool showMax)
SerializedProperty spGroupMaxId, bool showMax)
{ {
showMax |= spGroupId.intValue != spGroupMaxId.intValue || showMax |= spGroupId.intValue != spGroupMaxId.intValue ||
spGroupId.hasMultipleDifferentValues || spGroupId.hasMultipleDifferentValues ||
@ -449,10 +404,7 @@ namespace Coffee.UIExtensions
EditorGUI.BeginChangeCheck(); EditorGUI.BeginChangeCheck();
showMax = GUILayout.Toggle(showMax, s_ContentRandom, EditorStyles.miniButton, GUILayout.Width(60)); showMax = GUILayout.Toggle(showMax, s_ContentRandom, EditorStyles.miniButton, GUILayout.Width(60));
if (EditorGUI.EndChangeCheck() && !showMax) if (EditorGUI.EndChangeCheck() && !showMax)
{
spGroupMaxId.intValue = spGroupId.intValue; spGroupMaxId.intValue = spGroupId.intValue;
}
EditorGUILayout.EndHorizontal(); EditorGUILayout.EndHorizontal();
EditorGUI.BeginDisabledGroup(spMeshSharing.intValue == 0); EditorGUI.BeginDisabledGroup(spMeshSharing.intValue == 0);
@ -465,41 +417,20 @@ namespace Coffee.UIExtensions
else if (spMeshSharing.intValue == 1 || spMeshSharing.intValue == 4) else if (spMeshSharing.intValue == 1 || spMeshSharing.intValue == 4)
{ {
EditorGUI.BeginDisabledGroup(true); EditorGUI.BeginDisabledGroup(true);
var obj = UIParticleUpdater.GetPrimary(spGroupId.intValue); EditorGUILayout.ObjectField("Primary", UIParticleUpdater.GetPrimary(spGroupId.intValue), typeof(UIParticle), false);
EditorGUILayout.ObjectField("Primary", obj, typeof(UIParticle), false);
EditorGUI.EndDisabledGroup(); EditorGUI.EndDisabledGroup();
} }
EditorGUI.indentLevel--; EditorGUI.indentLevel--;
EditorGUI.EndDisabledGroup(); EditorGUI.EndDisabledGroup();
return showMax; return showMax;
} }
private static void DrawAutoScaling(SerializedProperty prop, IEnumerable<UIParticle> uiParticles) private static void WindowFunction(UnityEngine.Object target, SceneView sceneView)
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(prop);
if (!EditorGUI.EndChangeCheck()) return;
// on changed true->false, reset scale.
EditorApplication.delayCall += () =>
{
foreach (var uip in uiParticles)
{
if (!uip || uip.autoScaling) continue;
uip.transform.localScale = Vector3.one;
}
};
}
private static void WindowFunction(Object target, SceneView sceneView)
{ {
try try
{ {
if (s_SerializedObject == null || !s_SerializedObject.targetObject) return; if (s_SerializedObject.targetObjects.OfType<UIParticle>().Any(x => !x || !x.canvas)) return;
var uiParticles = s_SerializedObject.targetObjects.OfType<UIParticle>();
if (uiParticles.Any(x => !x || !x.canvas)) return;
s_SerializedObject.Update(); s_SerializedObject.Update();
using (new EditorGUILayout.VerticalScope(GUILayout.Width(220f))) using (new EditorGUILayout.VerticalScope(GUILayout.Width(220f)))
@ -507,36 +438,37 @@ namespace Coffee.UIExtensions
var labelWidth = EditorGUIUtility.labelWidth; var labelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 100; EditorGUIUtility.labelWidth = 100;
EditorGUILayout.PropertyField(s_SerializedObject.FindProperty("m_Enabled")); EditorGUILayout.PropertyField(s_SerializedObject.FindProperty("m_Enabled"));
s_XYZMode = DrawFloatOrVector3Field(s_SerializedObject.FindProperty("m_Scale3D"), s_XYZMode); _xyzMode = DrawFloatOrVector3Field(s_SerializedObject.FindProperty("m_Scale3D"), _xyzMode);
EditorGUILayout.PropertyField(s_SerializedObject.FindProperty("m_PositionMode")); EditorGUILayout.PropertyField(s_SerializedObject.FindProperty("m_AbsoluteMode"));
DrawAutoScaling(s_SerializedObject.FindProperty("m_AutoScaling"), uiParticles);
EditorGUIUtility.labelWidth = labelWidth; EditorGUIUtility.labelWidth = labelWidth;
} }
s_SerializedObject.ApplyModifiedProperties(); s_SerializedObject.ApplyModifiedProperties();
} }
catch catch
{ {
// ignored
} }
} }
private void DestroyUIParticle(UIParticle p, bool ignoreCurrent = false) private void DestroyUIParticle(UIParticle p, bool ignoreCurrent = false)
{ {
if (!p || (ignoreCurrent && target == p)) return; if (!p || ignoreCurrent && target == p) return;
var cr = p.canvasRenderer; var cr = p.canvasRenderer;
DestroyImmediate(p); DestroyImmediate(p);
DestroyImmediate(cr); DestroyImmediate(cr);
#if UNITY_2021_2_OR_NEWER
var stage = UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage();
#elif UNITY_2018_3_OR_NEWER
var stage = UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage();
#endif
#if UNITY_2018_3_OR_NEWER #if UNITY_2018_3_OR_NEWER
var stage = PrefabStageUtility.GetCurrentPrefabStage();
if (stage != null && stage.scene.isLoaded) if (stage != null && stage.scene.isLoaded)
{ {
#if UNITY_2020_1_OR_NEWER #if UNITY_2020_1_OR_NEWER
string prefabAssetPath = stage.assetPath; string prefabAssetPath = stage.assetPath;
#else #else
var prefabAssetPath = stage.prefabAssetPath; string prefabAssetPath = stage.prefabAssetPath;
#endif #endif
PrefabUtility.SaveAsPrefabAsset(stage.prefabContentsRoot, prefabAssetPath); PrefabUtility.SaveAsPrefabAsset(stage.prefabContentsRoot, prefabAssetPath);
} }
@ -585,10 +517,7 @@ namespace Coffee.UIExtensions
EditorGUI.BeginChangeCheck(); EditorGUI.BeginChangeCheck();
showXyz = GUILayout.Toggle(showXyz, s_Content3D, EditorStyles.miniButton, GUILayout.Width(30)); showXyz = GUILayout.Toggle(showXyz, s_Content3D, EditorStyles.miniButton, GUILayout.Width(30));
if (EditorGUI.EndChangeCheck() && !showXyz) if (EditorGUI.EndChangeCheck() && !showXyz)
{
z.floatValue = y.floatValue = x.floatValue; z.floatValue = y.floatValue = x.floatValue;
}
EditorGUILayout.EndHorizontal(); EditorGUILayout.EndHorizontal();
return showXyz; return showXyz;

View File

@ -34,10 +34,10 @@ namespace Coffee.UIExtensions
ps.transform.SetParent(uiParticle.transform, false); ps.transform.SetParent(uiParticle.transform, false);
ps.transform.localPosition = Vector3.zero; ps.transform.localPosition = Vector3.zero;
// Assign default material (UIAdditive). // Assign default material.
var renderer = ps.GetComponent<ParticleSystemRenderer>(); var renderer = ps.GetComponent<ParticleSystemRenderer>();
var path = AssetDatabase.GUIDToAssetPath("9944483a3e009401ba5dcc42f14d5c63"); var defaultMat = AssetDatabase.GetBuiltinExtraResource<Material>("Default-Particle.mat");
renderer.material = AssetDatabase.LoadAssetAtPath<Material>(path); renderer.material = defaultMat ? defaultMat : renderer.material;
// Refresh particles. // Refresh particles.
uiParticle.RefreshParticles(); uiParticle.RefreshParticles();

View File

@ -10,7 +10,7 @@ namespace Coffee.UIParticleExtensions
public static Material Add(Material baseMat, Texture texture, int id) public static Material Add(Material baseMat, Texture texture, int id)
{ {
MatEntry e; MatEntry e;
for (var i = 0; i < s_Entries.Count; i++) for (var i = 0; i < s_Entries.Count; ++i)
{ {
e = s_Entries[i]; e = s_Entries[i];
if (e.baseMat != baseMat || e.texture != texture || e.id != id) continue; if (e.baseMat != baseMat || e.texture != texture || e.id != id) continue;
@ -18,19 +18,15 @@ namespace Coffee.UIParticleExtensions
return e.customMat; return e.customMat;
} }
e = new MatEntry e = new MatEntry();
{ e.count = 1;
count = 1, e.baseMat = baseMat;
baseMat = baseMat, e.texture = texture;
texture = texture, e.id = id;
id = id, e.customMat = new Material(baseMat);
customMat = new Material(baseMat) e.customMat.hideFlags = HideFlags.HideAndDontSave;
{ if (texture)
name = $"{baseMat.name}_{id}", e.customMat.mainTexture = texture;
hideFlags = HideFlags.HideAndDontSave,
mainTexture = texture ? texture : null
}
};
s_Entries.Add(e); s_Entries.Add(e);
//Debug.LogFormat(">>>> ModifiedMaterial.Add -> count = count:{0}, mat:{1}, tex:{2}, id:{3}", s_Entries.Count, baseMat, texture, id); //Debug.LogFormat(">>>> ModifiedMaterial.Add -> count = count:{0}, mat:{1}, tex:{2}, id:{3}", s_Entries.Count, baseMat, texture, id);
return e.customMat; return e.customMat;
@ -47,8 +43,7 @@ namespace Coffee.UIParticleExtensions
if (--e.count == 0) 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); //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); DestroyImmediate(e.customMat);
e.customMat = null;
e.baseMat = null; e.baseMat = null;
e.texture = null; e.texture = null;
s_Entries.RemoveAt(i); s_Entries.RemoveAt(i);
@ -58,13 +53,22 @@ namespace Coffee.UIParticleExtensions
} }
} }
private static void DestroyImmediate(Object obj)
{
if (!obj) return;
if (Application.isEditor)
Object.DestroyImmediate(obj);
else
Object.Destroy(obj);
}
private class MatEntry private class MatEntry
{ {
public Material baseMat; public Material baseMat;
public int count;
public Material customMat; public Material customMat;
public int id; public int count;
public Texture texture; public Texture texture;
public int id;
} }
} }
} }

View File

@ -2,11 +2,11 @@
#define SERIALIZE_FIELD_MASKABLE #define SERIALIZE_FIELD_MASKABLE
#endif #endif
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Coffee.UIParticleExtensions; using Coffee.UIParticleExtensions;
using UnityEngine; using UnityEngine;
using UnityEngine.Rendering; using UnityEngine.Rendering;
using UnityEngine.Serialization;
using UnityEngine.UI; using UnityEngine.UI;
[assembly: InternalsVisibleTo("Coffee.UIParticle.Editor")] [assembly: InternalsVisibleTo("Coffee.UIParticle.Editor")]
@ -19,7 +19,7 @@ namespace Coffee.UIExtensions
[ExecuteAlways] [ExecuteAlways]
[RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(RectTransform))]
[RequireComponent(typeof(CanvasRenderer))] [RequireComponent(typeof(CanvasRenderer))]
public class UIParticle : MaskableGraphic, ISerializationCallbackReceiver public class UIParticle : MaskableGraphic
{ {
public enum MeshSharing public enum MeshSharing
{ {
@ -27,34 +27,16 @@ namespace Coffee.UIExtensions
Auto, Auto,
Primary, Primary,
PrimarySimulator, PrimarySimulator,
Replica Reprica,
} }
public enum PositionMode [HideInInspector][SerializeField] internal bool m_IsTrail = false;
{
Relative,
Absolute
}
[HideInInspector]
[SerializeField]
internal bool m_IsTrail;
[HideInInspector]
[FormerlySerializedAs("m_IgnoreParent")]
[SerializeField]
private bool m_IgnoreCanvasScaler;
[HideInInspector]
[SerializeField]
private bool m_AbsoluteMode;
[Tooltip("Particle effect scale")] [Tooltip("Particle effect scale")]
[SerializeField] [SerializeField]
private Vector3 m_Scale3D = new Vector3(10, 10, 10); private Vector3 m_Scale3D = new Vector3(10, 10, 10);
[Tooltip("Animatable material properties.\n" + [Tooltip("Animatable material properties. If you want to change the material properties of the ParticleSystem in Animation, enable it.")]
"If you want to change the material properties of the ParticleSystem in Animation, enable it.")]
[SerializeField] [SerializeField]
internal AnimatableProperty[] m_AnimatableProperties = new AnimatableProperty[0]; internal AnimatableProperty[] m_AnimatableProperties = new AnimatableProperty[0];
@ -62,44 +44,33 @@ namespace Coffee.UIExtensions
[SerializeField] [SerializeField]
private List<ParticleSystem> m_Particles = new List<ParticleSystem>(); private List<ParticleSystem> m_Particles = new List<ParticleSystem>();
[Tooltip("Mesh sharing.\n" + [Tooltip("Mesh sharing.None: disable mesh sharing.\nAuto: automatically select Primary/Reprica.\nPrimary: provides particle simulation results to the same group.\nPrimary Simulator: Primary, but do not render the particle (simulation only).\nReprica: render simulation results provided by the primary.")]
"None: disable mesh sharing.\n" +
"Auto: automatically select Primary/Replica.\n" +
"Primary: provides particle simulation results to the same group.\n" +
"Primary Simulator: Primary, but do not render the particle (simulation only).\n" +
"Replica: render simulation results provided by the primary.")]
[SerializeField] [SerializeField]
private MeshSharing m_MeshSharing = MeshSharing.None; private MeshSharing m_MeshSharing = MeshSharing.None;
[Tooltip("Mesh sharing group ID.\n" + [Tooltip("Mesh sharing group ID. If non-zero is specified, particle simulation results are shared within the group.")]
"If non-zero is specified, particle simulation results are shared within the group.")]
[SerializeField] [SerializeField]
private int m_GroupId; private int m_GroupId = 0;
[SerializeField] [SerializeField]
private int m_GroupMaxId; private int m_GroupMaxId = 0;
[Tooltip("Relative: The particles will be emitted from the scaled position of ParticleSystem.\n" +
"Absolute: The particles will be emitted from the world position of ParticleSystem.")]
[SerializeField]
private PositionMode m_PositionMode = PositionMode.Relative;
[SerializeField] [SerializeField]
[Tooltip("Transform.lossyScale (=world scale) is automatically set to (1, 1, 1), " + [Tooltip("The particles will be emitted at the ParticleSystem position.\nMove the UIParticle/ParticleSystem to move the particle.")]
"to prevent the root-Canvas scale from affecting the hierarchy-scaled ParticleSystem.")] private bool m_AbsoluteMode = false;
private bool m_AutoScaling = true;
private List<UIParticleRenderer> m_Renderers = new List<UIParticleRenderer>();
#if !SERIALIZE_FIELD_MASKABLE #if !SERIALIZE_FIELD_MASKABLE
[SerializeField] [SerializeField] private bool m_Maskable = true;
private bool m_Maskable = true;
#endif #endif
private readonly List<UIParticleRenderer> _renderers = new List<UIParticleRenderer>();
private int _groupId;
private Camera _orthoCamera;
private DrivenRectTransformTracker _tracker; private DrivenRectTransformTracker _tracker;
private Camera _orthoCamera;
private int _groupId;
/// <summary> /// <summary>
/// Should this graphic be considered a target for ray-casting? /// Should this graphic be considered a target for raycasting?
/// </summary> /// </summary>
public override bool raycastTarget public override bool raycastTarget
{ {
@ -108,12 +79,11 @@ namespace Coffee.UIExtensions
} }
/// <summary> /// <summary>
/// Mesh sharing. /// Mesh sharing.None: disable mesh sharing.
/// None: disable mesh sharing. /// Auto: automatically select Primary/Reprica.
/// Auto: automatically select Primary/Replica.
/// Primary: provides particle simulation results to the same group. /// Primary: provides particle simulation results to the same group.
/// Primary Simulator: Primary, but do not render the particle (simulation only). /// Primary Simulator: Primary, but do not render the particle (simulation only).
/// Replica: render simulation results provided by the primary. /// Reprica: render simulation results provided by the primary.
/// </summary> /// </summary>
public MeshSharing meshSharing public MeshSharing meshSharing
{ {
@ -122,8 +92,7 @@ namespace Coffee.UIExtensions
} }
/// <summary> /// <summary>
/// Mesh sharing group ID. /// Mesh sharing group ID. If non-zero is specified, particle simulation results are shared within the group.
/// If non-zero is specified, particle simulation results are shared within the group.
/// </summary> /// </summary>
public int groupId public int groupId
{ {
@ -133,11 +102,9 @@ namespace Coffee.UIExtensions
if (m_GroupId == value) return; if (m_GroupId == value) return;
m_GroupId = value; m_GroupId = value;
if (m_GroupId != m_GroupMaxId) if (m_GroupId != m_GroupMaxId)
{
ResetGroupId(); ResetGroupId();
} }
} }
}
public int groupMaxId public int groupMaxId
{ {
@ -151,40 +118,14 @@ namespace Coffee.UIExtensions
} }
/// <summary> /// <summary>
/// Particle position mode. /// Absolute particle position mode.
/// Relative: The particles will be emitted from the scaled position of the ParticleSystem. /// The particles will be emitted at the ParticleSystem position.
/// Absolute: The particles will be emitted from the world position of the ParticleSystem. /// Move the UIParticle/ParticleSystem to move the particle.
/// </summary>
public PositionMode positionMode
{
get { return m_PositionMode; }
set { m_PositionMode = value; }
}
/// <summary>
/// Particle position mode.
/// Relative: The particles will be emitted from the scaled position of the ParticleSystem.
/// Absolute: The particles will be emitted from the world position of the ParticleSystem.
/// </summary> /// </summary>
public bool absoluteMode public bool absoluteMode
{ {
get { return m_PositionMode == PositionMode.Absolute; } get { return m_AbsoluteMode; }
set { positionMode = value ? PositionMode.Absolute : PositionMode.Relative; } set { m_AbsoluteMode = value; }
}
/// <summary>
/// Transform.lossyScale (=world scale) will be set to (1, 1, 1) on update.
/// It prevents the root-Canvas scale from affecting the hierarchy-scaled ParticleSystem.
/// </summary>
public bool autoScaling
{
get { return m_AutoScaling; }
set
{
if (m_AutoScaling == value) return;
m_AutoScaling = value;
UpdateTracker();
}
} }
internal bool useMeshSharing internal bool useMeshSharing
@ -194,33 +135,17 @@ namespace Coffee.UIExtensions
internal bool isPrimary internal bool isPrimary
{ {
get get { return m_MeshSharing == MeshSharing.Primary || m_MeshSharing == MeshSharing.PrimarySimulator; }
{
return m_MeshSharing == MeshSharing.Primary
|| m_MeshSharing == MeshSharing.PrimarySimulator;
}
} }
internal bool canSimulate internal bool canSimulate
{ {
get get { return m_MeshSharing == MeshSharing.None || m_MeshSharing == MeshSharing.Auto || m_MeshSharing == MeshSharing.Primary || m_MeshSharing == MeshSharing.PrimarySimulator; }
{
return m_MeshSharing == MeshSharing.None
|| m_MeshSharing == MeshSharing.Auto
|| m_MeshSharing == MeshSharing.Primary
|| m_MeshSharing == MeshSharing.PrimarySimulator;
}
} }
internal bool canRender internal bool canRender
{ {
get get { return m_MeshSharing == MeshSharing.None || m_MeshSharing == MeshSharing.Auto || m_MeshSharing == MeshSharing.Primary || m_MeshSharing == MeshSharing.Reprica; }
{
return m_MeshSharing == MeshSharing.None
|| m_MeshSharing == MeshSharing.Auto
|| m_MeshSharing == MeshSharing.Primary
|| m_MeshSharing == MeshSharing.Replica;
}
} }
/// <summary> /// <summary>
@ -253,12 +178,12 @@ namespace Coffee.UIExtensions
{ {
get get
{ {
for (var i = 0; i < _renderers.Count; i++) for (var i = 0; i < m_Renderers.Count; i++)
{ {
var r = _renderers[i]; if (!m_Renderers[i] || !m_Renderers[i].material) continue;
if (!r || !r.material) continue; yield return m_Renderers[i].material;
yield return r.material;
} }
yield break;
} }
} }
@ -270,78 +195,7 @@ namespace Coffee.UIExtensions
/// <summary> /// <summary>
/// Paused. /// Paused.
/// </summary> /// </summary>
public bool isPaused { get; private set; } public bool isPaused { get; internal set; }
public Vector3 parentScale { get; private set; }
protected override void OnEnable()
{
#if !SERIALIZE_FIELD_MASKABLE
maskable = m_Maskable;
#endif
ResetGroupId();
UpdateTracker();
UIParticleUpdater.Register(this);
RegisterDirtyMaterialCallback(UpdateRendererMaterial);
if (0 < particles.Count)
{
RefreshParticles(particles);
}
else
{
RefreshParticles();
}
base.OnEnable();
}
/// <summary>
/// This function is called when the behaviour becomes disabled.
/// </summary>
protected override void OnDisable()
{
UpdateTracker();
UIParticleUpdater.Unregister(this);
_renderers.ForEach(r => r.Reset());
UnregisterDirtyMaterialCallback(UpdateRendererMaterial);
base.OnDisable();
}
/// <summary>
/// Callback for when properties have been changed by animation.
/// </summary>
protected override void OnDidApplyAnimationProperties()
{
}
#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
UpdateTracker();
}
#endif
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
}
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
if (m_IgnoreCanvasScaler)
{
m_IgnoreCanvasScaler = false;
m_AutoScaling = false;
}
if (m_AbsoluteMode)
{
m_AbsoluteMode = false;
m_PositionMode = PositionMode.Absolute;
}
}
public void Play() public void Play()
{ {
@ -366,24 +220,6 @@ namespace Coffee.UIExtensions
isPaused = true; isPaused = true;
} }
public void StartEmission()
{
particles.Exec(p =>
{
var emission = p.emission;
emission.enabled = true;
});
}
public void StopEmission()
{
particles.Exec(p =>
{
var emission = p.emission;
emission.enabled = false;
});
}
public void Clear() public void Clear()
{ {
particles.Exec(p => p.Clear()); particles.Exec(p => p.Clear());
@ -403,10 +239,14 @@ namespace Coffee.UIExtensions
{ {
var go = child.gameObject; var go = child.gameObject;
go.SetActive(false); go.SetActive(false);
if (destroyOldParticles) if (!destroyOldParticles) continue;
{
Misc.Destroy(go); #if UNITY_EDITOR
} if (!Application.isPlaying)
DestroyImmediate(go);
else
#endif
Destroy(go);
} }
var tr = instance.transform; var tr = instance.transform;
@ -431,60 +271,48 @@ namespace Coffee.UIExtensions
private void RefreshParticles(GameObject root) private void RefreshParticles(GameObject root)
{ {
if (!root) return; if (!root) return;
root.GetComponentsInChildren(true, particles); root.GetComponentsInChildren(particles);
particles.RemoveAll(x => x.GetComponentInParent<UIParticle>(true) != this); particles.RemoveAll(x => x.GetComponentInParent<UIParticle>() != this);
for (var i = 0; i < particles.Count; i++) foreach (var ps in particles)
{ {
var ps = particles[i];
var tsa = ps.textureSheetAnimation; var tsa = ps.textureSheetAnimation;
if (tsa.mode == ParticleSystemAnimationMode.Sprites && tsa.uvChannelMask == 0) if (tsa.mode == ParticleSystemAnimationMode.Sprites && tsa.uvChannelMask == 0)
{
tsa.uvChannelMask = UVChannelFlags.UV0; tsa.uvChannelMask = UVChannelFlags.UV0;
} }
}
RefreshParticles(particles); RefreshParticles(particles);
} }
public void RefreshParticles(List<ParticleSystem> particles) public void RefreshParticles(List<ParticleSystem> particles)
{ {
// #246: Nullptr exceptions when using nested UIParticle components in hierarchy GetComponentsInChildren(m_Renderers);
_renderers.Clear();
foreach (Transform child in transform)
{
var uiParticleRenderer = child.GetComponent<UIParticleRenderer>();
if (uiParticleRenderer != null)
{
_renderers.Add(uiParticleRenderer);
}
}
for (var i = 0; i < _renderers.Count; i++)
{
_renderers[i].Reset(i);
}
var j = 0; var j = 0;
for (var i = 0; i < particles.Count; i++) for (var i = 0; i < particles.Count; i++)
{ {
var ps = particles[i]; if (!particles[i]) continue;
if (!ps) continue; GetRenderer(j++).Set(this, particles[i], false);
GetRenderer(j++).Set(this, ps, false); if (particles[i].trails.enabled)
if (ps.trails.enabled)
{ {
GetRenderer(j++).Set(this, ps, true); GetRenderer(j++).Set(this, particles[i], true);
} }
} }
for (; j < m_Renderers.Count; j++)
{
GetRenderer(j).Clear(j);
}
} }
internal void UpdateTransformScale() internal void UpdateTransformScale()
{ {
parentScale = transform.parent.lossyScale; //var newScale = Vector3.one;
if (!autoScaling) return; //if (uiScaling)
//{
var newScale = parentScale.Inverse(); // newScale = transform.parent.lossyScale.Inverse();
//}
var newScale = transform.parent.lossyScale.Inverse();
if (transform.localScale != newScale) if (transform.localScale != newScale)
{ {
transform.localScale = newScale; transform.localScale = newScale;
@ -495,40 +323,73 @@ namespace Coffee.UIExtensions
{ {
if (!isActiveAndEnabled) return; if (!isActiveAndEnabled) return;
for (var i = 0; i < _renderers.Count; i++) if (m_Renderers.Any(x => !x))
{
var r = _renderers[i];
if (!r)
{ {
RefreshParticles(particles); RefreshParticles(particles);
break;
}
} }
var bakeCamera = GetBakeCamera(); var bakeCamera = GetBakeCamera();
for (var i = 0; i < _renderers.Count; i++) for (var i = 0; i < m_Renderers.Count; i++)
{ {
var r = _renderers[i]; if (!m_Renderers[i]) continue;
if (!r) continue; m_Renderers[i].UpdateMesh(bakeCamera);
r.UpdateMesh(bakeCamera);
} }
} }
internal void UpdateParticleCount() internal void UpdateParticleCount()
{ {
for (var i = 0; i < _renderers.Count; i++) for (var i = 0; i < m_Renderers.Count; i++)
{ {
var r = _renderers[i]; if (!m_Renderers[i]) continue;
if (!r) continue; m_Renderers[i].UpdateParticleCount();
r.UpdateParticleCount();
} }
} }
protected override void OnEnable()
{
#if !SERIALIZE_FIELD_MASKABLE
maskable = m_Maskable;
#endif
ResetGroupId();
_tracker.Add(this, rectTransform, DrivenTransformProperties.Scale);
UIParticleUpdater.Register(this);
RegisterDirtyMaterialCallback(UpdateRendererMaterial);
if (0 < particles.Count)
{
RefreshParticles(particles);
}
else
{
RefreshParticles();
}
base.OnEnable();
}
internal void ResetGroupId() internal void ResetGroupId()
{ {
_groupId = m_GroupId == m_GroupMaxId if (m_GroupId == m_GroupMaxId)
? m_GroupId {
: Random.Range(m_GroupId, m_GroupMaxId + 1); _groupId = m_GroupId;
}
else
{
_groupId = Random.Range(m_GroupId, m_GroupMaxId + 1);
}
}
/// <summary>
/// This function is called when the behaviour becomes disabled.
/// </summary>
protected override void OnDisable()
{
_tracker.Clear();
UIParticleUpdater.Unregister(this);
m_Renderers.ForEach(r => r.Clear());
UnregisterDirtyMaterialCallback(UpdateRendererMaterial);
base.OnDisable();
} }
protected override void UpdateMaterial() protected override void UpdateMaterial()
@ -542,61 +403,54 @@ namespace Coffee.UIExtensions
{ {
} }
/// <summary>
/// Callback for when properties have been changed by animation.
/// </summary>
protected override void OnDidApplyAnimationProperties()
{
}
private void UpdateRendererMaterial() private void UpdateRendererMaterial()
{ {
for (var i = 0; i < _renderers.Count; i++) for (var i = 0; i < m_Renderers.Count; i++)
{ {
var r = _renderers[i]; if (!m_Renderers[i]) continue;
if (!r) continue; m_Renderers[i].maskable = maskable;
r.maskable = maskable; m_Renderers[i].SetMaterialDirty();
r.SetMaterialDirty();
} }
} }
internal UIParticleRenderer GetRenderer(int index) internal UIParticleRenderer GetRenderer(int index)
{ {
if (_renderers.Count <= index) if (m_Renderers.Count <= index)
{ {
_renderers.Add(UIParticleRenderer.AddRenderer(this, index)); m_Renderers.Add(UIParticleRenderer.AddRenderer(this, index));
} }
if (!m_Renderers[index])
if (!_renderers[index])
{ {
_renderers[index] = UIParticleRenderer.AddRenderer(this, index); m_Renderers[index] = UIParticleRenderer.AddRenderer(this, index);
} }
return m_Renderers[index];
return _renderers[index];
} }
private Camera GetBakeCamera() private Camera GetBakeCamera()
{ {
if (!canvas) return Camera.main; if (!canvas) return Camera.main;
// When render mode is ScreenSpaceCamera or WorldSpace, use world camera. // World camera.
var root = canvas.rootCanvas; var root = canvas.rootCanvas;
if (root.renderMode != RenderMode.ScreenSpaceOverlay) if (root.renderMode != RenderMode.ScreenSpaceOverlay) return root.worldCamera ? root.worldCamera : Camera.main;
{
return root.worldCamera ? root.worldCamera : Camera.main;
}
// When render mode is ScreenSpaceOverlay, use ortho-camera.
if (!_orthoCamera)
{
// Find existing ortho-camera.
foreach (Transform child in transform)
{
var cam = child.GetComponent<Camera>();
if (cam && cam.name == "[generated] UIParticleOverlayCamera")
{
_orthoCamera = cam;
break;
}
}
// Create ortho-camera. // Create ortho-camera.
if (!_orthoCamera) if (!_orthoCamera)
{ {
var go = new GameObject("[generated] UIParticleOverlayCamera") { hideFlags = HideFlags.DontSave }; _orthoCamera = GetComponentInChildren<Camera>();
if (!_orthoCamera)
{
var go = new GameObject("UIParticleOverlayCamera")
{
hideFlags = HideFlags.DontSave,
};
go.SetActive(false); go.SetActive(false);
go.transform.SetParent(transform, false); go.transform.SetParent(transform, false);
_orthoCamera = go.AddComponent<Camera>(); _orthoCamera = go.AddComponent<Camera>();
@ -613,17 +467,5 @@ namespace Coffee.UIExtensions
return _orthoCamera; return _orthoCamera;
} }
private void UpdateTracker()
{
if (!enabled || !autoScaling)
{
_tracker.Clear();
}
else
{
_tracker.Add(this, rectTransform, DrivenTransformProperties.Scale);
}
}
} }
} }

View File

@ -1,7 +1,7 @@
using System; using UnityEngine;
using Coffee.UIParticleExtensions; using Coffee.UIParticleExtensions;
using UnityEngine;
using UnityEngine.Events; using UnityEngine.Events;
using System;
namespace Coffee.UIExtensions namespace Coffee.UIExtensions
{ {
@ -12,13 +12,7 @@ namespace Coffee.UIExtensions
{ {
Linear, Linear,
Smooth, Smooth,
Sphere Sphere,
}
public enum UpdateMode
{
Normal,
UnscaledTime
} }
[SerializeField] [SerializeField]
@ -30,7 +24,7 @@ namespace Coffee.UIExtensions
[Range(0f, 0.95f)] [Range(0f, 0.95f)]
[SerializeField] [SerializeField]
private float m_DelayRate; private float m_DelayRate = 0;
[Range(0.001f, 100f)] [Range(0.001f, 100f)]
[SerializeField] [SerializeField]
@ -39,79 +33,68 @@ namespace Coffee.UIExtensions
[SerializeField] [SerializeField]
private Movement m_Movement; private Movement m_Movement;
[SerializeField]
private UpdateMode m_UpdateMode;
[SerializeField] [SerializeField]
private UnityEvent m_OnAttracted; private UnityEvent m_OnAttracted;
private UIParticle _uiParticle;
public float destinationRadius
{
get { return m_DestinationRadius; }
set { m_DestinationRadius = Mathf.Clamp(value, 0.1f, 10f); }
}
public float delay public float delay
{ {
get { return m_DelayRate; } get
set { m_DelayRate = value; } {
return m_DelayRate;
}
set
{
m_DelayRate = value;
}
} }
public float maxSpeed public float maxSpeed
{ {
get { return m_MaxSpeed; } get
set { m_MaxSpeed = value; } {
return m_MaxSpeed;
}
set
{
m_MaxSpeed = value;
}
} }
public Movement movement public Movement movement
{ {
get { return m_Movement; } get
set { m_Movement = value; }
}
public UpdateMode updateMode
{ {
get { return m_UpdateMode; } return m_Movement;
set { m_UpdateMode = value; }
} }
public UnityEvent onAttracted
{
get { return m_OnAttracted; }
set { m_OnAttracted = value; }
}
#if UNITY_EDITOR
public new ParticleSystem particleSystem
#else
public ParticleSystem particleSystem
#endif
{
get { return m_ParticleSystem; }
set set
{ {
m_ParticleSystem = value; m_Movement = value;
ApplyParticleSystem();
} }
} }
private UIParticle _uiParticle;
private void OnEnable() private void OnEnable()
{ {
ApplyParticleSystem(); if (m_ParticleSystem == null)
{
Debug.LogError("No particle system attached to particle attractor script", this);
enabled = false;
return;
}
_uiParticle = m_ParticleSystem.GetComponentInParent<UIParticle>();
if (_uiParticle && !_uiParticle.particles.Contains(m_ParticleSystem))
{
_uiParticle = null;
}
UIParticleUpdater.Register(this); UIParticleUpdater.Register(this);
} }
private void OnDisable() private void OnDisable()
{
UIParticleUpdater.Unregister(this);
}
private void OnDestroy()
{ {
_uiParticle = null; _uiParticle = null;
m_ParticleSystem = null; UIParticleUpdater.Unregister(this);
} }
internal void Attract() internal void Attract()
@ -145,7 +128,6 @@ namespace Coffee.UIExtensions
Debug.LogException(e); Debug.LogException(e);
} }
} }
continue; continue;
} }
@ -172,51 +154,36 @@ namespace Coffee.UIExtensions
var psPos = m_ParticleSystem.transform.position; var psPos = m_ParticleSystem.transform.position;
var attractorPos = transform.position; var attractorPos = transform.position;
var dstPos = attractorPos; var dstPos = attractorPos;
var isLocalSpace = m_ParticleSystem.IsLocalSpace(); if (m_ParticleSystem.main.simulationSpace == ParticleSystemSimulationSpace.Local)
if (isLocalSpace)
{ {
dstPos = m_ParticleSystem.transform.InverseTransformPoint(dstPos); dstPos = m_ParticleSystem.transform.InverseTransformPoint(dstPos);
}
if (isUI) if (isUI)
{ {
var inverseScale = _uiParticle.parentScale.Inverse(); dstPos = dstPos.GetScaled(_uiParticle.transform.localScale, _uiParticle.scale3D.Inverse());
dstPos = dstPos.GetScaled(inverseScale, _uiParticle.scale3D.Inverse());
// Relative mode
if (_uiParticle.positionMode == UIParticle.PositionMode.Relative)
{
var diff = _uiParticle.transform.position - psPos;
diff.Scale(_uiParticle.scale3D - inverseScale);
diff.Scale(_uiParticle.scale3D.Inverse());
dstPos += diff;
} }
}
#if UNITY_EDITOR else
if (!Application.isPlaying && !isLocalSpace)
{ {
dstPos += psPos - psPos.GetScaled(inverseScale, _uiParticle.scale3D.Inverse()); #if UNITY_EDITOR
if (!Application.isPlaying && isUI)
{
var diff = dstPos - psPos;
diff = diff.GetScaled(_uiParticle.transform.localScale, _uiParticle.scale3D.Inverse());
return psPos + diff;
} }
#endif #endif
if (isUI)
{
dstPos.Scale(_uiParticle.transform.localScale);
dstPos.Scale(_uiParticle.scale3D.Inverse());
}
} }
return dstPos; return dstPos;
} }
private Vector3 GetAttractedPosition(Vector3 current, Vector3 target, float duration, float time) private Vector3 GetAttractedPosition(Vector3 current, Vector3 target, float duration, float time)
{ {
var speed = m_MaxSpeed; var speed = m_MaxSpeed;
switch (m_UpdateMode)
{
case UpdateMode.Normal:
speed *= 60 * Time.deltaTime;
break;
case UpdateMode.UnscaledTime:
speed *= 60 * Time.unscaledDeltaTime;
break;
}
switch (m_Movement) switch (m_Movement)
{ {
case Movement.Linear: case Movement.Linear:
@ -233,26 +200,5 @@ namespace Coffee.UIExtensions
return Vector3.MoveTowards(current, target, speed); return Vector3.MoveTowards(current, target, speed);
} }
private void ApplyParticleSystem()
{
_uiParticle = null;
if (m_ParticleSystem == null)
{
#if UNITY_EDITOR
if (Application.isPlaying)
#endif
{
Debug.LogError("No particle system attached to particle attractor script", this);
}
return;
}
_uiParticle = m_ParticleSystem.GetComponentInParent<UIParticle>(true);
if (_uiParticle && !_uiParticle.particles.Contains(m_ParticleSystem))
{
_uiParticle = null;
}
}
} }
} }

View File

@ -1,10 +1,9 @@
using System; using UnityEngine;
using System.Collections.Generic; using UnityEngine.UI;
using Coffee.UIParticleExtensions; using Coffee.UIParticleExtensions;
using UnityEngine;
using UnityEngine.Profiling; using UnityEngine.Profiling;
using UnityEngine.Rendering; using UnityEngine.Rendering;
using UnityEngine.UI; using System.Collections.Generic;
namespace Coffee.UIExtensions namespace Coffee.UIExtensions
{ {
@ -14,34 +13,41 @@ namespace Coffee.UIExtensions
[AddComponentMenu("")] [AddComponentMenu("")]
internal class UIParticleRenderer : MaskableGraphic internal class UIParticleRenderer : MaskableGraphic
{ {
private static readonly CombineInstance[] s_CombineInstances = { new CombineInstance() }; private static readonly CombineInstance[] s_CombineInstances = new CombineInstance[] { new CombineInstance() };
private static readonly List<Material> s_Materials = new List<Material>(2); private static readonly List<Material> s_Materials = new List<Material>(2);
private static MaterialPropertyBlock s_Mpb; private static MaterialPropertyBlock s_Mpb;
private static readonly List<UIParticleRenderer> s_Renderers = new List<UIParticleRenderer>(); private static readonly List<UIParticleRenderer> s_Renderers = new List<UIParticleRenderer>();
private static readonly Vector3[] s_Corners = new Vector3[4]; private static readonly Vector3[] s_Corners = new Vector3[4];
private Material _currentMaterialForRendering;
private bool _delay; private ParticleSystemRenderer _renderer;
private ParticleSystem _particleSystem;
private int _prevParticleCount = 0;
private UIParticle _parent;
private int _index; private int _index;
private bool _isTrail; private bool _isTrail;
private Bounds _lastBounds;
private Material _modifiedMaterial; private Material _modifiedMaterial;
private UIParticle _parent;
private ParticleSystem _particleSystem;
private int _prevParticleCount;
private Vector3 _prevPsPos;
private Vector3 _prevScale; private Vector3 _prevScale;
private Vector3 _prevPsPos;
private Vector2Int _prevScreenSize; private Vector2Int _prevScreenSize;
private bool _prewarm; private bool _delay = false;
private ParticleSystemRenderer _renderer; private bool _prewarm = false;
private Material _currentMaterialForRendering;
private Bounds _lastBounds;
public override Texture mainTexture public override Texture mainTexture
{ {
get { return _isTrail ? null : _particleSystem.GetTextureForSprite(); } get
{
return _isTrail ? null : _particleSystem.GetTextureForSprite();
}
} }
public override bool raycastTarget public override bool raycastTarget
{ {
get { return false; } get
{
return false;
}
} }
private Rect rootCanvasRect private Rect rootCanvasRect
@ -56,103 +62,32 @@ namespace Coffee.UIExtensions
{ {
var worldToLocalMatrix = canvas.rootCanvas.transform.worldToLocalMatrix; var worldToLocalMatrix = canvas.rootCanvas.transform.worldToLocalMatrix;
for (var i = 0; i < 4; ++i) for (var i = 0; i < 4; ++i)
{
s_Corners[i] = worldToLocalMatrix.MultiplyPoint(s_Corners[i]); s_Corners[i] = worldToLocalMatrix.MultiplyPoint(s_Corners[i]);
} }
} var corner1 = (Vector2) s_Corners[0];
var corner2 = (Vector2) s_Corners[0];
var corner1 = (Vector2)s_Corners[0];
var corner2 = (Vector2)s_Corners[0];
for (var i = 1; i < 4; ++i) for (var i = 1; i < 4; ++i)
{ {
if (s_Corners[i].x < corner1.x) if (s_Corners[i].x < corner1.x)
{
corner1.x = s_Corners[i].x; corner1.x = s_Corners[i].x;
}
else if (s_Corners[i].x > corner2.x) else if (s_Corners[i].x > corner2.x)
{
corner2.x = s_Corners[i].x; corner2.x = s_Corners[i].x;
}
if (s_Corners[i].y < corner1.y) if (s_Corners[i].y < corner1.y)
{
corner1.y = s_Corners[i].y; corner1.y = s_Corners[i].y;
}
else if (s_Corners[i].y > corner2.y) else if (s_Corners[i].y > corner2.y)
{
corner2.y = s_Corners[i].y; corner2.y = s_Corners[i].y;
} }
}
return new Rect(corner1, corner2 - corner1); return new Rect(corner1, corner2 - corner1);
} }
} }
public void Reset(int index = -1)
{
if (_renderer)
{
_renderer.enabled = true;
}
_parent = null;
_particleSystem = null;
_renderer = null;
_prevParticleCount = 0;
if (0 <= index)
{
_index = index;
}
//_emitter = null;
if (this && isActiveAndEnabled)
{
material = null;
workerMesh.Clear();
canvasRenderer.SetMesh(workerMesh);
_lastBounds = new Bounds();
enabled = false;
}
else
{
ModifiedMaterial.Remove(_modifiedMaterial);
_modifiedMaterial = null;
_currentMaterialForRendering = null;
}
}
protected override void OnEnable()
{
base.OnEnable();
if (!s_CombineInstances[0].mesh)
{
s_CombineInstances[0].mesh = new Mesh
{
name = "[UIParticleRenderer] Combine Instance Mesh",
hideFlags = HideFlags.HideAndDontSave
};
}
_currentMaterialForRendering = null;
}
protected override void OnDisable()
{
base.OnDisable();
ModifiedMaterial.Remove(_modifiedMaterial);
_modifiedMaterial = null;
_currentMaterialForRendering = null;
}
public static UIParticleRenderer AddRenderer(UIParticle parent, int index) public static UIParticleRenderer AddRenderer(UIParticle parent, int index)
{ {
// Create renderer object. // Create renderer object.
var go = new GameObject("[generated] UIParticleRenderer", typeof(UIParticleRenderer)) var go = new GameObject("UIParticleRenderer", typeof(UIParticleRenderer))
{ {
hideFlags = HideFlags.DontSave, hideFlags = HideFlags.DontSave,
layer = parent.gameObject.layer layer = parent.gameObject.layer,
}; };
// Set parent. // Set parent.
@ -177,12 +112,7 @@ namespace Coffee.UIExtensions
{ {
_currentMaterialForRendering = null; _currentMaterialForRendering = null;
if (!IsActive() || !_parent) if (!IsActive()) return baseMaterial;
{
ModifiedMaterial.Remove(_modifiedMaterial);
_modifiedMaterial = null;
return baseMaterial;
}
var modifiedMaterial = base.GetModifiedMaterial(baseMaterial); var modifiedMaterial = base.GetModifiedMaterial(baseMaterial);
@ -204,28 +134,50 @@ namespace Coffee.UIExtensions
return modifiedMaterial; return modifiedMaterial;
} }
public void Set(UIParticle parent, ParticleSystem ps, bool isTrail) public void Clear(int index = -1)
{
if (_renderer)
{
_renderer.enabled = true;
}
_parent = null;
_particleSystem = null;
_renderer = null;
_prevParticleCount = 0;
if (0 <= index)
{
_index = index;
}
//_emitter = null;
if (this && isActiveAndEnabled)
{
material = null;
workerMesh.Clear();
canvasRenderer.SetMesh(workerMesh);
_lastBounds = new Bounds();
enabled = false;
}
}
public void Set(UIParticle parent, ParticleSystem particleSystem, bool isTrail)
{ {
_parent = parent; _parent = parent;
maskable = parent.maskable; maskable = parent.maskable;
gameObject.layer = parent.gameObject.layer; gameObject.layer = parent.gameObject.layer;
_particleSystem = ps; _particleSystem = particleSystem;
_prewarm = _particleSystem.main.prewarm;
#if UNITY_EDITOR #if UNITY_EDITOR
if (Application.isPlaying) if (Application.isPlaying)
#endif #endif
{ if (_particleSystem.isPlaying)
if (_particleSystem.isPlaying || _prewarm)
{ {
_particleSystem.Clear(); _particleSystem.Clear();
_particleSystem.Pause(); _particleSystem.Pause();
} }
} _prewarm = _particleSystem.main.prewarm;
_renderer = ps.GetComponent<ParticleSystemRenderer>(); _renderer = particleSystem.GetComponent<ParticleSystemRenderer>();
_renderer.enabled = false; _renderer.enabled = false;
//_emitter = emitter; //_emitter = emitter;
@ -236,11 +188,9 @@ namespace Coffee.UIExtensions
s_Materials.Clear(); s_Materials.Clear();
// Support sprite. // Support sprite.
var tsa = ps.textureSheetAnimation; var tsa = particleSystem.textureSheetAnimation;
if (tsa.mode == ParticleSystemAnimationMode.Sprites && tsa.uvChannelMask == 0) if (tsa.mode == ParticleSystemAnimationMode.Sprites && tsa.uvChannelMask == 0)
{
tsa.uvChannelMask = UVChannelFlags.UV0; tsa.uvChannelMask = UVChannelFlags.UV0;
}
_prevScale = GetWorldScale(); _prevScale = GetWorldScale();
_prevPsPos = _particleSystem.transform.position; _prevPsPos = _particleSystem.transform.position;
@ -257,15 +207,13 @@ namespace Coffee.UIExtensions
{ {
// No particle to render: Clear mesh. // No particle to render: Clear mesh.
if ( if (
!isActiveAndEnabled || !_particleSystem || !_parent !isActiveAndEnabled || !_particleSystem || !_parent || !canvasRenderer || !canvas || !bakeCamera
|| !canvasRenderer || !canvas || !bakeCamera || _parent.meshSharing == UIParticle.MeshSharing.Reprica
|| _parent.meshSharing == UIParticle.MeshSharing.Replica
|| !transform.lossyScale.GetScaled(_parent.scale3D).IsVisible() // Scale is not visible. || !transform.lossyScale.GetScaled(_parent.scale3D).IsVisible() // Scale is not visible.
|| (!_particleSystem.IsAlive() && !_particleSystem.isPlaying) // No particle. || (!_particleSystem.IsAlive() && !_particleSystem.isPlaying) // No particle.
|| (_isTrail && !_particleSystem.trails.enabled) // Trail, but it is not enabled. || (_isTrail && !_particleSystem.trails.enabled) // Trail, but it is not enabled.
#if UNITY_2018_3_OR_NEWER #if UNITY_2018_3_OR_NEWER
|| canvasRenderer.GetInheritedAlpha() < || canvasRenderer.GetInheritedAlpha() < 0.01f // #102: Do not bake particle system to mesh when the alpha is zero.
0.01f // #102: Do not bake particle system to mesh when the alpha is zero.
#endif #endif
) )
{ {
@ -303,25 +251,20 @@ namespace Coffee.UIExtensions
} }
// When the ParticleSystem simulation is complete, stop it. // When the ParticleSystem simulation is complete, stop it.
if (!main.loop if (!main.loop && main.duration <= _particleSystem.time && (_particleSystem.IsAlive() || _particleSystem.particleCount == 0))
&& main.duration <= _particleSystem.time
&& (_particleSystem.IsAlive() || _particleSystem.particleCount == 0)
)
{ {
_particleSystem.Stop(false); _particleSystem.Stop(false);
} }
} }
_prevScale = scale; _prevScale = scale;
_prevPsPos = psPos; _prevPsPos = psPos;
_delay = false; _delay = false;
} }
Profiler.EndSample(); Profiler.EndSample();
// Bake mesh. // Bake mesh.
Profiler.BeginSample("[UIParticleRenderer] Bake Mesh"); Profiler.BeginSample("[UIParticleRenderer] Bake Mesh");
if (_isTrail && _parent.canSimulate && 0 < s_CombineInstances[0].mesh.vertexCount) if (_isTrail && _parent.canSimulate)
{ {
_renderer.BakeTrailsMesh(s_CombineInstances[0].mesh, bakeCamera, true); _renderer.BakeTrailsMesh(s_CombineInstances[0].mesh, bakeCamera, true);
} }
@ -333,41 +276,21 @@ namespace Coffee.UIExtensions
{ {
s_CombineInstances[0].mesh.Clear(false); s_CombineInstances[0].mesh.Clear(false);
} }
// Too many vertices to render.
if (65535 <= s_CombineInstances[0].mesh.vertexCount)
{
s_CombineInstances[0].mesh.Clear(false);
Debug.LogErrorFormat(this,
"Too many vertices to render. index={0}, isTrail={1}, vertexCount={2}(>=65535)",
_index,
_isTrail,
s_CombineInstances[0].mesh.vertexCount
);
s_CombineInstances[0].mesh.Clear(false);
}
Profiler.EndSample(); Profiler.EndSample();
// Combine mesh to transform. ([ParticleSystem local ->] world -> renderer local) // Combine mesh to transform. ([ParticleSystem local ->] world -> renderer local)
Profiler.BeginSample("[UIParticleRenderer] Combine Mesh"); Profiler.BeginSample("[UIParticleRenderer] Combine Mesh");
if (_parent.canSimulate) if (_parent.canSimulate)
{ {
if (_parent.positionMode == UIParticle.PositionMode.Absolute) if (_parent.absoluteMode)
{ {
s_CombineInstances[0].transform = s_CombineInstances[0].transform = canvasRenderer.transform.worldToLocalMatrix * GetWorldMatrix(psPos, scale);
canvasRenderer.transform.worldToLocalMatrix
* GetWorldMatrix(psPos, scale);
} }
else else
{ {
var diff = _particleSystem.transform.position - _parent.transform.position; var diff = _particleSystem.transform.position - _parent.transform.position;
s_CombineInstances[0].transform = s_CombineInstances[0].transform = canvasRenderer.transform.worldToLocalMatrix * Matrix4x4.Translate(diff.GetScaled(scale - Vector3.one)) * GetWorldMatrix(psPos, scale);
canvasRenderer.transform.worldToLocalMatrix
* Matrix4x4.Translate(diff.GetScaled(scale - Vector3.one))
* GetWorldMatrix(psPos, scale);
} }
workerMesh.CombineMeshes(s_CombineInstances, true, true); workerMesh.CombineMeshes(s_CombineInstances, true, true);
workerMesh.RecalculateBounds(); workerMesh.RecalculateBounds();
@ -381,7 +304,6 @@ namespace Coffee.UIExtensions
workerMesh.bounds = bounds; workerMesh.bounds = bounds;
_lastBounds = bounds; _lastBounds = bounds;
} }
Profiler.EndSample(); Profiler.EndSample();
@ -394,7 +316,7 @@ namespace Coffee.UIExtensions
// Set mesh to the CanvasRenderer. // Set mesh to the CanvasRenderer.
Profiler.BeginSample("[UIParticleRenderer] Set Mesh"); Profiler.BeginSample("[UIParticleRenderer] Set Mesh");
for (var i = 0; i < s_Renderers.Count; i++) for (int i = 0; i < s_Renderers.Count; i++)
{ {
if (s_Renderers[i] == this) continue; if (s_Renderers[i] == this) continue;
s_Renderers[i].canvasRenderer.SetMesh(workerMesh); s_Renderers[i].canvasRenderer.SetMesh(workerMesh);
@ -405,7 +327,6 @@ namespace Coffee.UIExtensions
{ {
workerMesh.Clear(); workerMesh.Clear();
} }
canvasRenderer.SetMesh(workerMesh); canvasRenderer.SetMesh(workerMesh);
Profiler.EndSample(); Profiler.EndSample();
@ -418,8 +339,7 @@ namespace Coffee.UIExtensions
{ {
_currentMaterialForRendering = materialForRendering; _currentMaterialForRendering = materialForRendering;
} }
for (int i = 0; i < s_Renderers.Count; i++)
for (var i = 0; i < s_Renderers.Count; i++)
{ {
if (s_Renderers[i] == this) continue; if (s_Renderers[i] == this) continue;
@ -427,7 +347,6 @@ namespace Coffee.UIExtensions
s_Renderers[i].canvasRenderer.SetMaterial(_currentMaterialForRendering, 0); s_Renderers[i].canvasRenderer.SetMaterial(_currentMaterialForRendering, 0);
} }
} }
Profiler.EndSample(); Profiler.EndSample();
s_Renderers.Clear(); s_Renderers.Clear();
@ -439,6 +358,30 @@ namespace Coffee.UIExtensions
_prevParticleCount = _particleSystem.particleCount; _prevParticleCount = _particleSystem.particleCount;
} }
protected override void OnEnable()
{
base.OnEnable();
if (!s_CombineInstances[0].mesh)
{
s_CombineInstances[0].mesh = new Mesh()
{
name = "[UIParticleRenderer] Combine Instance Mesh",
hideFlags = HideFlags.HideAndDontSave,
};
}
_currentMaterialForRendering = null;
}
protected override void OnDisable()
{
base.OnDisable();
ModifiedMaterial.Remove(_modifiedMaterial);
_modifiedMaterial = null;
_currentMaterialForRendering = null;
}
/// <summary> /// <summary>
/// Call to update the geometry of the Graphic onto the CanvasRenderer. /// Call to update the geometry of the Graphic onto the CanvasRenderer.
/// </summary> /// </summary>
@ -448,9 +391,7 @@ namespace Coffee.UIExtensions
public override void Cull(Rect clipRect, bool validRect) public override void Cull(Rect clipRect, bool validRect)
{ {
var cull = _lastBounds.extents == Vector3.zero var cull = _lastBounds.extents == Vector3.zero || !validRect || !clipRect.Overlaps(rootCanvasRect, true);
|| !validRect
|| !clipRect.Overlaps(rootCanvasRect, true);
if (canvasRenderer.cull == cull) return; if (canvasRenderer.cull == cull) return;
canvasRenderer.cull = cull; canvasRenderer.cull = cull;
@ -462,7 +403,16 @@ namespace Coffee.UIExtensions
private Vector3 GetWorldScale() private Vector3 GetWorldScale()
{ {
Profiler.BeginSample("[UIParticleRenderer] GetWorldScale"); Profiler.BeginSample("[UIParticleRenderer] GetWorldScale");
var scale = _parent.scale3D.GetScaled(_parent.parentScale); var scale = _parent.scale3D;
//if (_parent.uiScaling)
{
scale.Scale(_parent.transform.localScale.Inverse());
}
//else if (_parent.scalingMode == UIParticle.ScalingMode.UI && _particleSystem.main.scalingMode != ParticleSystemScalingMode.Hierarchy)
//{
// var gscale = _parent.transform.lossyScale.GetScaled(canvas.transform.lossyScale.Inverse());
// scale.Scale(gscale * canvas.scaleFactor);
//}
Profiler.EndSample(); Profiler.EndSample();
return scale; return scale;
} }
@ -502,7 +452,7 @@ namespace Coffee.UIExtensions
//* Matrix4x4.Translate(-wpos) //* Matrix4x4.Translate(-wpos)
; ;
default: default:
throw new NotSupportedException(); throw new System.NotSupportedException();
} }
} }
@ -514,9 +464,8 @@ namespace Coffee.UIExtensions
private void ResolveResolutionChange(Vector3 psPos, Vector3 scale) private void ResolveResolutionChange(Vector3 psPos, Vector3 scale)
{ {
var screenSize = new Vector2Int(Screen.width, Screen.height); var screenSize = new Vector2Int(Screen.width, Screen.height);
var isWorldSpace = _particleSystem.IsWorldSpace(); //if ((_prevScreenSize != screenSize || _prevScale != scale) && _particleSystem.main.simulationSpace == ParticleSystemSimulationSpace.World && _parent.uiScaling)
var resolutionChanged = _prevScreenSize != screenSize || _prevScale != scale; if ((_prevScreenSize != screenSize || _prevScale != scale) && _particleSystem.main.simulationSpace == ParticleSystemSimulationSpace.World)
if (resolutionChanged && isWorldSpace)
{ {
// Update particle array size and get particles. // Update particle array size and get particles.
var size = _particleSystem.particleCount; var size = _particleSystem.particleCount;
@ -525,17 +474,13 @@ namespace Coffee.UIExtensions
// Resolusion resolver: // Resolusion resolver:
// (psPos / scale) / (prevPsPos / prevScale) -> psPos * scale.inv * prevPsPos.inv * prevScale // (psPos / scale) / (prevPsPos / prevScale) -> psPos * scale.inv * prevPsPos.inv * prevScale
var modifier = psPos.GetScaled( var modifier = psPos.GetScaled(scale.Inverse(), _prevPsPos.Inverse(), _prevScale);
scale.Inverse(),
_prevPsPos.Inverse(),
_prevScale);
for (var i = 0; i < size; i++) for (var i = 0; i < size; i++)
{ {
var particle = particles[i]; var particle = particles[i];
particle.position = particle.position.GetScaled(modifier); particle.position = particle.position.GetScaled(modifier);
particles[i] = particle; particles[i] = particle;
} }
_particleSystem.SetParticles(particles, size); _particleSystem.SetParticles(particles, size);
// Delay: Do not progress in the frame where the resolution has been changed. // Delay: Do not progress in the frame where the resolution has been changed.
@ -543,7 +488,6 @@ namespace Coffee.UIExtensions
_prevScale = scale; _prevScale = scale;
_prevPsPos = psPos; _prevPsPos = psPos;
} }
_prevScreenSize = screenSize; _prevScreenSize = screenSize;
} }
@ -563,8 +507,6 @@ namespace Coffee.UIExtensions
_prewarm = false; _prewarm = false;
} }
// (COMMENT OUT) #231: Sub Emitters option is not work in editor playing
/*
// Emitted particles found. // Emitted particles found.
if (_prevParticleCount != _particleSystem.particleCount) if (_prevParticleCount != _particleSystem.particleCount)
{ {
@ -580,31 +522,24 @@ namespace Coffee.UIExtensions
_particleSystem.SetParticles(particles, size); _particleSystem.SetParticles(particles, size);
} }
*/
// get world position. // get world position.
var isLocalSpace = _particleSystem.IsLocalSpace();
var psTransform = _particleSystem.transform; var psTransform = _particleSystem.transform;
var originWorldPosition = psTransform.position; var originWorldPosition = psTransform.position;
var originWorldRotation = psTransform.rotation; var originWorldRotation = psTransform.rotation;
var emission = _particleSystem.emission; var emission = _particleSystem.emission;
var rateOverDistance = emission.enabled var rateOverDistance = emission.enabled && 0 < emission.rateOverDistance.constant && 0 < emission.rateOverDistanceMultiplier;
&& 0 < emission.rateOverDistance.constant if (rateOverDistance)
&& 0 < emission.rateOverDistanceMultiplier;
if (rateOverDistance && !paused)
{ {
// (For rate-over-distance emission,) Move to previous scaled position, simulate (delta = 0). // (For rate-over-distance emission,) Move to previous scaled position, simulate (delta = 0).
var prevScaledPos = isLocalSpace Vector3 prevScaledPos = _prevPsPos.GetScaled(_prevScale.Inverse());
? _prevPsPos
: _prevPsPos.GetScaled(_prevScale.Inverse());
psTransform.SetPositionAndRotation(prevScaledPos, originWorldRotation); psTransform.SetPositionAndRotation(prevScaledPos, originWorldRotation);
_particleSystem.Simulate(0, false, false, false); _particleSystem.Simulate(0, false, false, false);
} }
// Move to scaled position, simulate, revert to origin position. // Move to scaled position, simulate, revert to origin position.
var scaledPos = isLocalSpace var scaledPos = originWorldPosition.GetScaled(scale.Inverse());
? originWorldPosition
: originWorldPosition.GetScaled(scale.Inverse());
psTransform.SetPositionAndRotation(scaledPos, originWorldRotation); psTransform.SetPositionAndRotation(scaledPos, originWorldRotation);
_particleSystem.Simulate(deltaTime, false, false, false); _particleSystem.Simulate(deltaTime, false, false, false);
psTransform.SetPositionAndRotation(originWorldPosition, originWorldRotation); psTransform.SetPositionAndRotation(originWorldPosition, originWorldRotation);
@ -614,8 +549,7 @@ namespace Coffee.UIExtensions
private void SimulateForEditor(Vector3 diffPos, Vector3 scale) private void SimulateForEditor(Vector3 diffPos, Vector3 scale)
{ {
// Extra world simulation. // Extra world simulation.
var isWorldSpace = _particleSystem.IsWorldSpace(); if (_particleSystem.main.simulationSpace == ParticleSystemSimulationSpace.World && 0 < Vector3.SqrMagnitude(diffPos))
if (isWorldSpace && 0 < Vector3.SqrMagnitude(diffPos))
{ {
Profiler.BeginSample("[UIParticle] Bake Mesh > Extra world simulation"); Profiler.BeginSample("[UIParticle] Bake Mesh > Extra world simulation");
diffPos.x *= 1f - 1f / Mathf.Max(0.001f, scale.x); diffPos.x *= 1f - 1f / Mathf.Max(0.001f, scale.x);
@ -643,9 +577,7 @@ namespace Coffee.UIExtensions
if (_parent.m_AnimatableProperties.Length == 0) return; if (_parent.m_AnimatableProperties.Length == 0) return;
if (s_Mpb == null) if (s_Mpb == null)
{
s_Mpb = new MaterialPropertyBlock(); s_Mpb = new MaterialPropertyBlock();
}
_renderer.GetPropertyBlock(s_Mpb); _renderer.GetPropertyBlock(s_Mpb);
if (s_Mpb.isEmpty) return; if (s_Mpb.isEmpty) return;
@ -653,9 +585,8 @@ namespace Coffee.UIExtensions
// #41: Copy the value from MaterialPropertyBlock to CanvasRenderer // #41: Copy the value from MaterialPropertyBlock to CanvasRenderer
if (!_modifiedMaterial) return; if (!_modifiedMaterial) return;
for (var i = 0; i < _parent.m_AnimatableProperties.Length; i++) foreach (var ap in _parent.m_AnimatableProperties)
{ {
var ap = _parent.m_AnimatableProperties[i];
ap.UpdateMaterialProperties(_modifiedMaterial, s_Mpb); ap.UpdateMaterialProperties(_modifiedMaterial, s_Mpb);
} }

View File

@ -1,19 +1,21 @@
using System.Collections.Generic; using System.Collections.Generic;
using UnityEditor;
using UnityEngine; using UnityEngine;
namespace Coffee.UIExtensions namespace Coffee.UIExtensions
{ {
internal static class UIParticleUpdater internal static class UIParticleUpdater
{ {
private static readonly List<UIParticle> s_ActiveParticles = new List<UIParticle>(); static readonly List<UIParticle> s_ActiveParticles = new List<UIParticle>();
private static readonly List<UIParticleAttractor> s_ActiveAttractors = new List<UIParticleAttractor>(); static readonly List<UIParticleAttractor> s_ActiveAttractors = new List<UIParticleAttractor>();
private static readonly HashSet<int> s_UpdatedGroupIds = new HashSet<int>(); static readonly HashSet<int> s_UpdatedGroupIds = new HashSet<int>();
private static int s_FrameCount; private static int frameCount = 0;
public static int uiParticleCount public static int uiParticleCount
{ {
get { return s_ActiveParticles.Count; } get
{
return s_ActiveParticles.Count;
}
} }
public static void Register(UIParticle particle) public static void Register(UIParticle particle)
@ -41,7 +43,7 @@ namespace Coffee.UIExtensions
} }
#if UNITY_EDITOR #if UNITY_EDITOR
[InitializeOnLoadMethod] [UnityEditor.InitializeOnLoadMethod]
#endif #endif
[RuntimeInitializeOnLoadMethod] [RuntimeInitializeOnLoadMethod]
private static void InitializeOnLoad() private static void InitializeOnLoad()
@ -53,8 +55,8 @@ namespace Coffee.UIExtensions
private static void Refresh() private static void Refresh()
{ {
// Do not allow it to be called in the same frame. // Do not allow it to be called in the same frame.
if (s_FrameCount == Time.frameCount) return; if (frameCount == Time.frameCount) return;
s_FrameCount = Time.frameCount; frameCount = Time.frameCount;
// Simulate -> Primary // Simulate -> Primary
for (var i = 0; i < s_ActiveParticles.Count; i++) for (var i = 0; i < s_ActiveParticles.Count; i++)
@ -127,7 +129,6 @@ namespace Coffee.UIExtensions
if (uip.isPrimary) return uip; if (uip.isPrimary) return uip;
if (!primary && uip.canSimulate) primary = uip; if (!primary && uip.canSimulate) primary = uip;
} }
return primary; return primary;
} }
} }

View File

@ -46,11 +46,11 @@ namespace Coffee.UIParticleExtensions
internal static class SpriteExtensions internal static class SpriteExtensions
{ {
#if UNITY_EDITOR #if UNITY_EDITOR
private static readonly Type s_SpriteEditorExtensionType = private static Type tSpriteEditorExtension =
Type.GetType("UnityEditor.Experimental.U2D.SpriteEditorExtension, UnityEditor") Type.GetType("UnityEditor.Experimental.U2D.SpriteEditorExtension, UnityEditor")
?? Type.GetType("UnityEditor.U2D.SpriteEditorExtension, UnityEditor"); ?? Type.GetType("UnityEditor.U2D.SpriteEditorExtension, UnityEditor");
private static readonly MethodInfo s_GetActiveAtlasTextureMethodInfo = s_SpriteEditorExtensionType private static MethodInfo miGetActiveAtlasTexture = tSpriteEditorExtension
.GetMethod("GetActiveAtlasTexture", BindingFlags.Static | BindingFlags.NonPublic); .GetMethod("GetActiveAtlasTexture", BindingFlags.Static | BindingFlags.NonPublic);
public static Texture2D GetActualTexture(this Sprite self) public static Texture2D GetActualTexture(this Sprite self)
@ -58,10 +58,8 @@ namespace Coffee.UIParticleExtensions
if (!self) return null; if (!self) return null;
if (Application.isPlaying) return self.texture; if (Application.isPlaying) return self.texture;
var ret = s_GetActiveAtlasTextureMethodInfo.Invoke(null, new object[] { self }) as Texture2D; var ret = miGetActiveAtlasTexture.Invoke(null, new[] { self }) as Texture2D;
return ret return ret ? ret : self.texture;
? ret
: self.texture;
} }
#else #else
internal static Texture2D GetActualTexture(this Sprite self) internal static Texture2D GetActualTexture(this Sprite self)
@ -79,14 +77,12 @@ namespace Coffee.UIParticleExtensions
{ {
if (s_TmpParticles.Length < size) if (s_TmpParticles.Length < size)
{ {
while (s_TmpParticles.Length < size) while(s_TmpParticles.Length < size)
{ {
size = Mathf.NextPowerOfTwo(size); size = Mathf.NextPowerOfTwo(size);
} }
s_TmpParticles = new ParticleSystem.Particle[size]; s_TmpParticles = new ParticleSystem.Particle[size];
} }
return s_TmpParticles; return s_TmpParticles;
} }
@ -106,69 +102,47 @@ namespace Coffee.UIParticleExtensions
var main = self.main; var main = self.main;
var space = main.simulationSpace; var space = main.simulationSpace;
if (space == ParticleSystemSimulationSpace.Custom && !main.customSimulationSpace) if (space == ParticleSystemSimulationSpace.Custom && !main.customSimulationSpace)
{
space = ParticleSystemSimulationSpace.Local; space = ParticleSystemSimulationSpace.Local;
}
return space; return space;
} }
public static bool IsLocalSpace(this ParticleSystem self)
{
return GetActualSimulationSpace(self) == ParticleSystemSimulationSpace.Local;
}
public static bool IsWorldSpace(this ParticleSystem self)
{
return GetActualSimulationSpace(self) == ParticleSystemSimulationSpace.World;
}
public static void SortForRendering(this List<ParticleSystem> self, Transform transform, bool sortByMaterial) public static void SortForRendering(this List<ParticleSystem> self, Transform transform, bool sortByMaterial)
{ {
self.Sort((a, b) => self.Sort((a, b) =>
{ {
var tr = transform;
var aRenderer = a.GetComponent<ParticleSystemRenderer>(); var aRenderer = a.GetComponent<ParticleSystemRenderer>();
var bRenderer = b.GetComponent<ParticleSystemRenderer>(); var bRenderer = b.GetComponent<ParticleSystemRenderer>();
// Render queue: ascending // Render queue: ascending
var aMat = aRenderer.sharedMaterial ? aRenderer.sharedMaterial : aRenderer.trailMaterial; var aMat = aRenderer.sharedMaterial ?? aRenderer.trailMaterial;
var bMat = bRenderer.sharedMaterial ? bRenderer.sharedMaterial : bRenderer.trailMaterial; var bMat = bRenderer.sharedMaterial ?? bRenderer.trailMaterial;
if (!aMat && !bMat) return 0; if (!aMat && !bMat) return 0;
if (!aMat) return -1; if (!aMat) return -1;
if (!bMat) return 1; if (!bMat) return 1;
if (sortByMaterial) if (sortByMaterial)
{
return aMat.GetInstanceID() - bMat.GetInstanceID(); return aMat.GetInstanceID() - bMat.GetInstanceID();
}
if (aMat.renderQueue != bMat.renderQueue) if (aMat.renderQueue != bMat.renderQueue)
{
return aMat.renderQueue - bMat.renderQueue; return aMat.renderQueue - bMat.renderQueue;
}
// Sorting layer: ascending // Sorting layer: ascending
if (aRenderer.sortingLayerID != bRenderer.sortingLayerID) if (aRenderer.sortingLayerID != bRenderer.sortingLayerID)
{ return SortingLayer.GetLayerValueFromID(aRenderer.sortingLayerID) - SortingLayer.GetLayerValueFromID(bRenderer.sortingLayerID);
return SortingLayer.GetLayerValueFromID(aRenderer.sortingLayerID) -
SortingLayer.GetLayerValueFromID(bRenderer.sortingLayerID);
}
// Sorting order: ascending // Sorting order: ascending
if (aRenderer.sortingOrder != bRenderer.sortingOrder) if (aRenderer.sortingOrder != bRenderer.sortingOrder)
{
return aRenderer.sortingOrder - bRenderer.sortingOrder; return aRenderer.sortingOrder - bRenderer.sortingOrder;
}
// Z position & sortingFudge: descending // Z position & sortingFudge: descending
var aTransform = a.transform; var aTransform = a.transform;
var bTransform = b.transform; var bTransform = b.transform;
var aPos = transform.InverseTransformPoint(aTransform.position).z + aRenderer.sortingFudge; var aPos = tr.InverseTransformPoint(aTransform.position).z + aRenderer.sortingFudge;
var bPos = transform.InverseTransformPoint(bTransform.position).z + bRenderer.sortingFudge; var bPos = tr.InverseTransformPoint(bTransform.position).z + bRenderer.sortingFudge;
if (!Mathf.Approximately(aPos, bPos)) if (!Mathf.Approximately(aPos, bPos))
{
return (int)Mathf.Sign(bPos - aPos); return (int)Mathf.Sign(bPos - aPos);
}
return (int)Mathf.Sign(GetIndex(self, a) - GetIndex(self, b)); return (int)Mathf.Sign(GetIndex(self, a) - GetIndex(self, b));
}); });
@ -178,10 +152,7 @@ namespace Coffee.UIParticleExtensions
{ {
for (var i = 0; i < list.Count; i++) for (var i = 0; i < list.Count; i++)
{ {
if (list[i].GetInstanceID() == ps.GetInstanceID()) if (list[i].GetInstanceID() == ps.GetInstanceID()) return i;
{
return i;
}
} }
return 0; return 0;
@ -212,55 +183,4 @@ namespace Coffee.UIParticleExtensions
self.ForEach(action); self.ForEach(action);
} }
} }
internal static class Misc
{
public static void Destroy(Object obj)
{
if (!obj) return;
#if UNITY_EDITOR
if (!Application.isPlaying)
{
Object.DestroyImmediate(obj);
}
else
#endif
{
Object.Destroy(obj);
}
}
public static void DestroyImmediate(Object obj)
{
if (!obj) return;
#if UNITY_EDITOR
if (Application.isEditor)
{
Object.DestroyImmediate(obj);
}
else
#endif
{
Object.Destroy(obj);
}
}
#if !UNITY_2021_2_OR_NEWER || UNITY_2020_3_45 || UNITY_2020_3_46 || UNITY_2020_3_47 || UNITY_2020_3_48
public static T GetComponentInParent<T>(this Component self, bool includeInactive) where T : Component
{
if (!self) return null;
if (!includeInactive) return self.GetComponentInParent<T>();
var current = self.transform;
while (current)
{
var component = current.GetComponent<T>();
if (component) return component;
current = current.parent;
}
return null;
}
#endif
}
} }

View File

@ -1,85 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: UIAdditive
m_Shader: {fileID: 4800000, guid: ecfa8f5732b504ef98fba10aa18d0326, type: 3}
m_ShaderKeywords:
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 10300, guid: 0000000000000000f000000000000000, type: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _BumpScale: 1
- _ColorMask: 15
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _Stencil: 0
- _StencilComp: 8
- _StencilOp: 0
- _StencilReadMask: 255
- _StencilWriteMask: 255
- _UVSec: 0
- _UseUIAlphaClip: 0
- _ZWrite: 1
m_Colors:
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
m_BuildTextureStacks: []

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 9944483a3e009401ba5dcc42f14d5c63
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -2,7 +2,7 @@
"name": "com.coffee.ui-particle", "name": "com.coffee.ui-particle",
"displayName": "UI Particle", "displayName": "UI Particle",
"description": "This plugin provide a component to render particle effect for uGUI.\nThe particle rendering is maskable and sortable, without Camera, RenderTexture or Canvas.", "description": "This plugin provide a component to render particle effect for uGUI.\nThe particle rendering is maskable and sortable, without Camera, RenderTexture or Canvas.",
"version": "4.2.1", "version": "4.1.7",
"unity": "2018.2", "unity": "2018.2",
"license": "MIT", "license": "MIT",
"repository": { "repository": {