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.
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Environment (please complete the following)**
- Package version [e.g. 4.2.0, 3.3.14]
**Environment (please complete the following information):**
- Version [e.g. 1.0.0]
- Platform: [e.g. Editor(Windows/Mac), Standalone(Windows/Mac), iOS, Android, WebGL]
- Unity version: [e.g. 2021.3.7f1]
- Project options: [e.g. Mono/IL2CPP, .Net Standard 2.1/.Net 4.x, URP/HDRP, GraphicAPIs]
- Unity version: [e.g. 2018.2.8f1]
- Build options: [e.g. IL2CPP, .Net 4.x, LWRP]
**Additional context**
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`
Add any other context about the problem here.

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.
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the issue is. (e.g. I'm always frustrated when [...])
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

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

View File

@ -36,7 +36,7 @@ jobs:
steps:
# Checkout sandbox project
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
ref: sandbox
submodules: true
@ -48,7 +48,7 @@ jobs:
run: git checkout ${{ github.sha }}
# Cache
- uses: actions/cache@v3
- uses: actions/cache@v2
with:
path: Library
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)

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:

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.
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/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&sort=semver&filter=3.%2A&label=release(3.x))](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/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)
![](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) >>
@ -56,8 +58,6 @@ Compares this "Baking mesh" approach with the conventional approach:
* Support CanvasGroup alpha
* No allocations
* 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)
![](https://user-images.githubusercontent.com/12690315/53286323-2d94a980-37b0-11e9-8afb-c4a207805ff2.gif)
* [4.0.0+] Support 8+ materials
@ -92,41 +92,39 @@ Compares this "Baking mesh" approach with the conventional approach:
### 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).
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
```
### Install via Package Manager Window (using Git URL)
### Using Git
1. Select `Window/Package Manager` menu to open `Package Manager` window.
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)
Find the manifest.json file in the Packages folder of your project and add a line to `dependencies` field.
* `"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.
### Install via manifest.json (using Git URL)
### For Unity 2018.2
1. Open `Packages/manifest.json` in your project.
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
Unity 2018.2 supports embedded packages.
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.
![](https://github.com/mob-sakai/ParticleEffectForUGUI/assets/12690315/4c3d3439-5bb0-4e87-a917-ebe328ef89a8)
2. Extract it
3. Import it under `Packages` directory in your Unity project
<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.
![](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 |
| -- | -- |
| 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. |
| 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. |
| Position Mode | **Absolute:** Emit from the world position of the `ParticleSystem`.<br>**Relative:** Emit from the scaled position of the `ParticleSystem`. |
| 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 ParticleSystem list to be rendered.<br>You can change the order and the materials. |
| Absolute Mode | The particles will be emitted at the ParticleSystem position.<br>Move the UIParticle/ParticleSystem to move the particle. |
| Rendering Order | The ParticleSystems to be rendered.<br>You can change the rendering order and the materials. |
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.
![](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)
| 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. |
| 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) |
| 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). |
<br><br><br><br>
## 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
The use of UI shaders is recommended.
- 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).
UIParticles are based on UIVertex.
Therefore, only xy components is available for each UV in the shader. (zw components will be ignored).
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.
https://github.com/mob-sakai/ParticleEffectForUGUI/issues/191
<br>
- 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.
### Overheads

View File

@ -1,51 +1,33 @@
using System;
using System.Linq;
using System.Reflection;
using Coffee.UIExtensions;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Object = UnityEngine.Object;
namespace Coffee.UIExtensions.Demo
{
public class CFX_Demo_With_UIParticle : MonoBehaviour
{
private MonoBehaviour _demo;
private Toggle _spawnOnUI;
private UIParticle _uiParticle;
private UIParticle UiParticle;
private Toggle spawnOnUI;
private MonoBehaviour demo;
// Start is called before the first frame update
private void Start()
{
_uiParticle = GetComponentInChildren<UIParticle>();
_spawnOnUI = GetComponentInChildren<Toggle>();
_demo = FindObjectOfType("CFX_Demo_New") as MonoBehaviour
UiParticle = GetComponentInChildren<UIParticle>();
spawnOnUI = GetComponentInChildren<Toggle>();
demo = FindObjectOfType("CFX_Demo_New") as MonoBehaviour
?? FindObjectOfType("WFX_Demo_New") as MonoBehaviour;
SetCanvasWidth(800);
SetCanvasRenderOverlay(true);
}
// 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, Array.Empty<object>()) as GameObject;
if (!particle) return;
particle.transform.localScale = Vector3.one;
_uiParticle.SetParticleSystemInstance(particle);
}
private static Object FindObjectOfType(string typeName)
private Object FindObjectOfType(string typeName)
{
var type = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
@ -54,6 +36,23 @@ namespace Coffee.UIExtensions.Demo
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)
{
var scaler = GetComponentInParent<CanvasScaler>();
@ -84,4 +83,3 @@ namespace Coffee.UIExtensions.Demo
SceneManager.LoadScene(scene);
}
}
}

View File

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

View File

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

View File

@ -1,18 +1,14 @@
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
namespace Coffee.UIExtensions.Demo
{
public class UIParticle_Demo : MonoBehaviour
{
[FormerlySerializedAs("root")]
[SerializeField]
private Canvas m_RootCanvas;
public Canvas root;
private int _height;
private int _score;
private int _width;
private int _height;
private void Start()
{
@ -28,18 +24,11 @@ namespace Coffee.UIExtensions.Demo
case RuntimePlatform.WindowsPlayer:
case RuntimePlatform.LinuxPlayer:
if (Screen.width == _width && Screen.height == _height)
{
Screen.SetResolution(_height, _width, Screen.fullScreen);
}
else if (Screen.width == _height && Screen.height == _width)
{
Screen.SetResolution(Mathf.Min(_width, _height), Mathf.Min(_width, _height), Screen.fullScreen);
}
else
{
Screen.SetResolution(_width, _height, Screen.fullScreen);
}
break;
}
}
@ -49,31 +38,27 @@ namespace Coffee.UIExtensions.Demo
Screen.fullScreen = !Screen.fullScreen;
}
public void EnableAnimations(bool flag)
public void EnableAnimations(bool enabled)
{
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
? UIParticle.MeshSharing.Auto
: UIParticle.MeshSharing.None;
uip.meshSharing = enabled ? 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
? 4
: 0;
uip.groupMaxId = enabled ? 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;
main.simulationSpace = flag
? ParticleSystemSimulationSpace.World
: ParticleSystemSimulationSpace.Local;
var main = ps.main;
main.simulationSpace = enabled ? 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;
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;
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)
@ -144,37 +127,41 @@ namespace Coffee.UIExtensions.Demo
attractor.movement = UIParticleAttractor.Movement.Sphere;
}
int score = 0;
public void UIParticleAttractor_OnAttract(Text scoreText)
{
_score++;
scoreText.text = _score.ToString();
score++;
scoreText.text = score.ToString();
scoreText.GetComponent<Animator>().Play(0);
}
public void Canvas_WorldSpace(bool flag)
{
if (!flag) return;
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) return;
if (flag)
{
var canvas = FindObjectOfType<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceCamera;
}
}
public void Canvas_Overlay(bool flag)
{
if (!flag) return;
if (flag)
{
var canvas = FindObjectOfType<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
}
}
}
}

View File

@ -1,37 +1,30 @@
using UnityEngine;
using UnityEngine.Serialization;
namespace Coffee.UIExtensions.Demo
{
public class UIParticle_Demo_UIParticleController : MonoBehaviour
{
[FormerlySerializedAs("root")]
[SerializeField]
private Transform m_RootTransform;
public Transform root;
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
? UIParticle.MeshSharing.Auto
: UIParticle.MeshSharing.None;
uip.meshSharing = enabled ? 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
? 4
: 0;
uip.groupMaxId = enabled ? 4 : 0;
}
}
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;
}

View File

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

View File

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

View File

@ -1,11 +1,10 @@
using System;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.Serialization;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditorInternal;
#endif
namespace Coffee.NanoMonitor
@ -14,29 +13,24 @@ namespace Coffee.NanoMonitor
[CustomEditor(typeof(NanoMonitor))]
internal class NanoMonitorEditor : Editor
{
private ReorderableList _monitorItemList;
private UnityEditorInternal.ReorderableList m_MonitorItemList;
private void OnEnable()
{
var items = serializedObject.FindProperty("m_CustomMonitorItems");
_monitorItemList = new ReorderableList(serializedObject, items)
m_MonitorItemList = new UnityEditorInternal.ReorderableList(serializedObject, items)
{
draggable = false,
drawHeaderCallback = r =>
{
EditorGUI.LabelField(r, new GUIContent("Custom Monitor Items"));
},
drawHeaderCallback = r => { EditorGUI.LabelField(r, new GUIContent("Custom Monitor Items")); },
drawElementCallback = (r, i, _, __) =>
{
EditorGUI.LabelField(new Rect(r.x, r.y, r.width, r.height - 2), GUIContent.none,
EditorStyles.textArea);
EditorGUI.LabelField(new Rect(r.x, r.y, r.width, r.height - 2), GUIContent.none, EditorStyles.textArea);
var labelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 80;
EditorGUI.PropertyField(new Rect(r.x + 2, r.y + 3, r.width - 4, r.height - 4),
items.GetArrayElementAtIndex(i), true);
EditorGUI.PropertyField(new Rect(r.x + 2, r.y + 3, r.width - 4, r.height - 4), items.GetArrayElementAtIndex(i), true);
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();
_monitorItemList.DoLayoutList();
m_MonitorItemList.DoLayoutList();
serializedObject.ApplyModifiedProperties();
}
}
@ -58,50 +52,38 @@ namespace Coffee.NanoMonitor
// Serialize Members.
//################################
// Settings
[Header("Settings")]
[SerializeField]
[Range(0.01f, 2f)]
[Header("Settings")] [SerializeField] [Range(0.01f, 2f)]
private float m_Interval = 1f;
[SerializeField]
[Range(0, 3)]
private int m_Precision = 2;
[SerializeField]
private Font m_Font;
[SerializeField] [Range(0, 3)] private int m_Precision = 2;
[SerializeField] private Font m_Font;
// Foldout
[Header("Foldout")]
[SerializeField]
private bool m_Opened;
[Header("Foldout")] [SerializeField] private bool m_Opened = false;
[FormerlySerializedAs("m_Collapse")]
[SerializeField]
private GameObject m_FoldoutObject;
[FormerlySerializedAs("m_Collapse")] [SerializeField]
private GameObject m_FoldoutObject = null;
[SerializeField]
private Button m_OpenButton;
[SerializeField]
private Button m_CloseButton;
[SerializeField] private Button m_OpenButton = null;
[SerializeField] private Button m_CloseButton = null;
// View
[Header("View")]
[SerializeField]
private MonitorUI m_Fps;
[Header("View")] [SerializeField] private MonitorUI m_Fps = null;
[SerializeField] private MonitorUI m_Gc = null;
[SerializeField] private MonitorUI m_MonoUsage = null;
[SerializeField] private MonitorUI m_UnityUsage = null;
[SerializeField]
private MonitorUI m_Gc;
[HideInInspector] [SerializeField] private CustomMonitorItem[] m_CustomMonitorItems = new CustomMonitorItem[0];
[SerializeField]
private MonitorUI m_MonoUsage;
[SerializeField]
private MonitorUI m_UnityUsage;
[HideInInspector]
[SerializeField]
private CustomMonitorItem[] m_CustomMonitorItems = new CustomMonitorItem[0];
//################################
// Public Members.
//################################
public void Clean()
{
Resources.UnloadUnusedAssets();
GC.Collect();
}
//################################
@ -111,75 +93,44 @@ namespace Coffee.NanoMonitor
private double _fpsElapsed;
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;
_elapsed %= m_Interval;
_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);
}
}
@ -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
private void OnValidate()
{
@ -242,55 +264,5 @@ namespace Coffee.NanoMonitor
}
}
#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.UI;
namespace Coffee.NanoMonitor
{
namespace Coffee.NanoMonitor{
public class FixedFont
{
private const string k_Characters =
"_!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
private readonly UIVertex[] _tmpVerts = new UIVertex[4];
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 readonly TextGenerator s_TextGenerator = new TextGenerator(100);
private static TextGenerationSettings s_Settings = new TextGenerationSettings
private static TextGenerationSettings _settings = new TextGenerationSettings
{
scaleFactor = 1,
horizontalOverflow = HorizontalWrapMode.Overflow,
@ -22,33 +19,25 @@ namespace Coffee.NanoMonitor
color = Color.white
};
private int _resolution = 0;
private readonly Font _font;
private readonly UIVertex[] _tmpVerts = new UIVertex[4];
private UICharInfo[] _charInfos;
private int _resolution;
private UIVertex[] _verts;
private UICharInfo[] _charInfos;
private FixedFont(Font font)
{
_font = font;
}
public int fontSize
{
get
{
return _font.dynamic ? 32 : _font.fontSize;
}
}
public int fontSize => _font.dynamic ? 32 : _font.fontSize;
public static FixedFont GetOrCreate(Font font)
{
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);
s_Fonts.Add(font, data);
_fonts.Add(font, data);
return data;
}
@ -72,20 +61,18 @@ namespace Coffee.NanoMonitor
if (_resolution == currentResolution) return;
_resolution = currentResolution;
s_Settings.font = _font;
s_TextGenerator.Invalidate();
s_TextGenerator.Populate(k_Characters, s_Settings);
_settings.font = _font;
_textGenerator.Invalidate();
_textGenerator.Populate("_!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", _settings);
_verts = s_TextGenerator.GetVerticesArray();
_charInfos = s_TextGenerator.GetCharactersArray();
_verts = _textGenerator.GetVerticesArray();
_charInfos = _textGenerator.GetCharactersArray();
float offsetX = 0;
for (var i = 0; i < _verts.Length; i++)
{
if ((i & 3) == 0)
{
offsetX = _verts[i].position.x;
}
var v = _verts[i];
v.position -= new Vector3(offsetX, 0);
@ -131,27 +118,18 @@ namespace Coffee.NanoMonitor
for (var i = 0; i < 4; i++)
{
_tmpVerts[i] = new UIVertex
{
uv0 = uv,
color = color
};
_tmpVerts[i] = new UIVertex();
_tmpVerts[i].uv0 = uv;
_tmpVerts[i].color = color;
switch (i)
{
case 0:
if (i == 0)
_tmpVerts[i].position = new Vector2(-size.x, -size.y) + offset;
break;
case 1:
else if (i == 1)
_tmpVerts[i].position = new Vector2(-size.x, size.y) + offset;
break;
case 2:
else if (i == 2)
_tmpVerts[i].position = new Vector2(size.x, size.y) + offset;
break;
case 3:
else
_tmpVerts[i].position = new Vector2(size.x, -size.y) + offset;
break;
}
}
toFill.AddUIVertexQuad(_tmpVerts);

View File

@ -1,46 +1,50 @@
using System.Text;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
namespace Coffee.NanoMonitor
{
namespace Coffee.NanoMonitor{
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(MonitorUI))]
public class MonitorTextEditor : Editor
{
private SerializedProperty _color;
private SerializedProperty _font;
private SerializedProperty _fontSize;
private SerializedProperty _mode;
private SerializedProperty _text;
private SerializedProperty _textAnchor;
private SerializedProperty m_Mode;
private SerializedProperty m_TextAnchor;
private SerializedProperty m_FontSize;
private SerializedProperty m_Font;
private SerializedProperty m_Text;
private SerializedProperty m_Color;
private void OnEnable()
{
_mode = serializedObject.FindProperty("m_Mode");
_text = serializedObject.FindProperty("m_Text");
_color = serializedObject.FindProperty("m_Color");
m_Mode = serializedObject.FindProperty("m_Mode");
m_Text = serializedObject.FindProperty("m_Text");
m_Color = serializedObject.FindProperty("m_Color");
var fontData = serializedObject.FindProperty("m_FontData");
_fontSize = fontData.FindPropertyRelative("m_FontSize");
_font = fontData.FindPropertyRelative("m_Font");
_textAnchor = serializedObject.FindProperty("m_TextAnchor");
m_FontSize = fontData.FindPropertyRelative("m_FontSize");
m_Font = fontData.FindPropertyRelative("m_Font");
m_TextAnchor = serializedObject.FindProperty("m_TextAnchor");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(_mode);
if ((MonitorUI.Mode)_mode.intValue == MonitorUI.Mode.Text)
EditorGUILayout.PropertyField(m_Mode);
if ((MonitorUI.Mode) m_Mode.intValue == MonitorUI.Mode.Text)
{
EditorGUILayout.PropertyField(_text);
EditorGUILayout.PropertyField(_fontSize);
EditorGUILayout.PropertyField(_textAnchor);
EditorGUILayout.PropertyField(_font);
EditorGUILayout.PropertyField(m_Text);
EditorGUILayout.PropertyField(m_FontSize);
EditorGUILayout.PropertyField(m_TextAnchor);
//EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.PropertyField(m_Font);
//EditorGUI.EndDisabledGroup();
}
EditorGUILayout.PropertyField(_color);
EditorGUILayout.PropertyField(m_Color);
serializedObject.ApplyModifiedProperties();
}
@ -52,7 +56,7 @@ namespace Coffee.NanoMonitor
public enum Mode
{
Text,
Fill
Fill,
}
public enum TextAnchor
@ -70,38 +74,26 @@ namespace Coffee.NanoMonitor
[SerializeField] private TextAnchor m_TextAnchor;
//################################
// Private Members.
//################################
private readonly StringBuilder _sb = new StringBuilder(64);
//################################
// Public Members.
//################################
public override string text
{
get
{
return _sb.ToString();
}
get => m_StringBuilder.ToString();
set
{
m_Text = value;
if (_sb.IsEqual(m_Text)) return;
if (m_StringBuilder.IsEqual(m_Text)) return;
_sb.Length = 0;
_sb.Append(m_Text);
m_StringBuilder.Length = 0;
m_StringBuilder.Append(m_Text);
SetVerticesDirty();
}
}
public TextAnchor textAnchor
{
get
{
return m_TextAnchor;
}
get => m_TextAnchor;
set
{
if (m_TextAnchor == value) return;
@ -113,30 +105,65 @@ namespace Coffee.NanoMonitor
public override bool raycastTarget
{
get
{
return m_Mode == Mode.Fill;
}
get => m_Mode == Mode.Fill;
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.
//################################
protected override void OnEnable()
{
//NaNanoMonitortings.Instance.onFontChanged += UpdateFont;
RegisterDirtyMaterialCallback(UpdateFont);
base.OnEnable();
raycastTarget = false;
maskable = false;
_sb.Length = 0;
_sb.Append(m_Text);
m_StringBuilder.Length = 0;
m_StringBuilder.Append(m_Text);
}
protected override void OnDisable()
{
//NanNanoMonitorings.Instance.onFontChanged -= UpdateFont;
UnregisterDirtyMaterialCallback(UpdateFont);
base.OnDisable();
@ -147,38 +174,14 @@ namespace Coffee.NanoMonitor
{
base.OnValidate();
if (!_sb.IsEqual(m_Text))
if (!m_StringBuilder.IsEqual(m_Text))
{
_sb.Length = 0;
_sb.Append(m_Text);
m_StringBuilder.Length = 0;
m_StringBuilder.Append(m_Text);
}
}
#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)
{
toFill.Clear();
@ -202,26 +205,20 @@ namespace Coffee.NanoMonitor
offset = rectTransform.rect.xMin;
break;
case TextAnchor.Center:
for (var i = 0; i < _sb.Length; i++)
{
offset = fontData.Layout(_sb[i], offset, scale);
}
for (var i = 0; i < m_StringBuilder.Length; i++)
offset = fontData.Layout(m_StringBuilder[i], offset, scale);
offset = -offset / 2;
break;
case TextAnchor.Right:
for (var i = 0; i < _sb.Length; i++)
{
offset = fontData.Layout(_sb[i], offset, scale);
}
for (var i = 0; i < m_StringBuilder.Length; i++)
offset = fontData.Layout(m_StringBuilder[i], offset, scale);
offset = rectTransform.rect.xMax - offset;
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
using UnityEditor;
#endif
@ -12,17 +11,12 @@ namespace Coffee.NanoMonitor
{
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(pos, property.FindPropertyRelative("m_Text"));
pos.y += 18;
EditorGUI.PropertyField(pos, property.FindPropertyRelative("m_Format"));
pos.y += 18;
EditorGUI.PropertyField(new Rect(p.x, p.y + 18 * 0, p.width, 16), property.FindPropertyRelative("m_Text"));
EditorGUI.PropertyField(new Rect(p.x, p.y + 18 * 1, p.width, 16), property.FindPropertyRelative("m_Format"));
EditorGUI.indentLevel++;
EditorGUI.PropertyField(pos, property.FindPropertyRelative("m_Arg0"));
pos.y += 18;
EditorGUI.PropertyField(pos, property.FindPropertyRelative("m_Arg1"));
pos.y += 18;
EditorGUI.PropertyField(pos, property.FindPropertyRelative("m_Arg2"));
EditorGUI.PropertyField(new Rect(p.x, p.y + 18 * 2, p.width, 16), property.FindPropertyRelative("m_Arg0"));
EditorGUI.PropertyField(new Rect(p.x, p.y + 18 * 3, p.width, 16), property.FindPropertyRelative("m_Arg1"));
EditorGUI.PropertyField(new Rect(p.x, p.y + 18 * 4, p.width, 16), property.FindPropertyRelative("m_Arg2"));
EditorGUI.indentLevel--;
property.serializedObject.ApplyModifiedProperties();
@ -35,21 +29,19 @@ namespace Coffee.NanoMonitor
}
#endif
[Serializable]
[System.Serializable]
public class CustomMonitorItem
{
[SerializeField] private MonitorUI m_Text;
[SerializeField] private MonitorUI m_Text = null;
[SerializeField] private string m_Format = "";
[SerializeField] private NumericProperty m_Arg0;
[SerializeField] private NumericProperty m_Arg1;
[SerializeField] private NumericProperty m_Arg2;
[SerializeField] private NumericProperty m_Arg0 = null;
[SerializeField] private NumericProperty m_Arg1 = null;
[SerializeField] private NumericProperty m_Arg2 = null;
public void UpdateText()
{
if (m_Text)
{
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.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityEngine.Serialization;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Coffee.NanoMonitor
{
@ -27,34 +31,28 @@ namespace Coffee.NanoMonitor
{typeof(ulong), "ulong"},
{typeof(float), "float"},
{typeof(double), "double"},
{ typeof(decimal), "decimal" }
{typeof(decimal), "decimal"},
};
private static void Init()
{
if (s_PropertyMenu != null) return;
const BindingFlags bindingFlags = BindingFlags.Public
| BindingFlags.NonPublic
| BindingFlags.Static
| BindingFlags.GetProperty;
var properties = AppDomain.CurrentDomain.GetAssemblies()
.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))
.OrderBy(pi => ConvertToMenuItem(pi, false))
.ToArray();
s_PropertyMenu = new GenericMenu();
s_PropertyMenu.AddItem(new GUIContent("No Property"), false,
arg => s_OnMenuSelected?.Invoke(arg as PropertyInfo), null);
s_PropertyMenu.AddItem(new GUIContent("No Property"), false, arg => s_OnMenuSelected?.Invoke(arg as PropertyInfo), null);
s_PropertyMenu.AddItem(new GUIContent("(Non Public Properties)/"), false, ()=>{});
s_PropertyMenu.AddSeparator("");
foreach (var pi in properties)
{
s_PropertyMenu.AddItem(new GUIContent(ConvertToMenuItem(pi, true)), false,
arg => s_OnMenuSelected?.Invoke(arg as PropertyInfo), pi);
s_PropertyMenu.AddItem(new GUIContent(ConvertToMenuItem(pi, true)), false, arg => s_OnMenuSelected?.Invoke(arg as PropertyInfo), pi);
}
}
@ -62,17 +60,11 @@ namespace Coffee.NanoMonitor
{
var type = p.DeclaringType;
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 asmName = type.Assembly.GetName().Name;
if (asmName == "UnityEngine.CoreModule")
{
asmName = "UnityEngine";
}
return propertyType
? $"{category}{asmName}/{typeName}/{s_SupportedTypes[p.PropertyType]} {p.Name}"
: $"{category}{asmName}/{typeName}/{p.Name}";
@ -92,9 +84,7 @@ namespace Coffee.NanoMonitor
{
s_OnMenuSelected = p =>
{
path.stringValue = p == null
? ""
: $"{p.DeclaringType?.FullName}, {p.DeclaringType?.Assembly.GetName().Name};{p.Name}";
path.stringValue = p == null ? "" : $"{p.DeclaringType?.FullName}, {p.DeclaringType?.Assembly.GetName().Name};{p.Name}";
property.serializedObject.ApplyModifiedProperties();
};
s_PropertyMenu.DropDown(position);
@ -114,22 +104,6 @@ namespace Coffee.NanoMonitor
[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.
//################################
@ -138,6 +112,12 @@ namespace Coffee.NanoMonitor
return _get?.Invoke() ?? -1;
}
//################################
// Private Members.
//################################
private Func<double> _get = null;
private static PropertyInfo GetPropertyInfo(string path)
{
var p = path.Split(';');
@ -146,14 +126,14 @@ namespace Coffee.NanoMonitor
var type = Type.GetType(p[0]);
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;
}
var pInfo = type.GetProperty(p[1], BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Static);
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;
@ -165,72 +145,55 @@ namespace Coffee.NanoMonitor
switch (Type.GetTypeCode(mInfo.ReturnType))
{
case TypeCode.Boolean:
{
var func = (Func<bool>)mInfo.CreateDelegate(typeof(Func<bool>));
return () => func() ? 1 : 0;
}
var f_bool = (Func<bool>) mInfo.CreateDelegate(typeof(Func<bool>));
return () => f_bool() ? 1 : 0;
case TypeCode.Byte:
{
var func = (Func<byte>)mInfo.CreateDelegate(typeof(Func<byte>));
return () => func();
}
var f_byte = (Func<byte>) mInfo.CreateDelegate(typeof(Func<byte>));
return () => f_byte();
case TypeCode.SByte:
{
var func = (Func<sbyte>)mInfo.CreateDelegate(typeof(Func<sbyte>));
return () => func();
}
var f_sbyte = (Func<sbyte>) mInfo.CreateDelegate(typeof(Func<sbyte>));
return () => f_sbyte();
case TypeCode.UInt16:
{
var func = (Func<ushort>)mInfo.CreateDelegate(typeof(Func<ushort>));
return () => func();
}
var f_ushort = (Func<ushort>) mInfo.CreateDelegate(typeof(Func<ushort>));
return () => f_ushort();
case TypeCode.UInt32:
{
var func = (Func<uint>)mInfo.CreateDelegate(typeof(Func<uint>));
return () => func();
}
var f_uint = (Func<uint>) mInfo.CreateDelegate(typeof(Func<uint>));
return () => f_uint();
case TypeCode.UInt64:
{
var func = (Func<ulong>)mInfo.CreateDelegate(typeof(Func<ulong>));
return () => func();
}
var f_ulong = (Func<ulong>) mInfo.CreateDelegate(typeof(Func<ulong>));
return () => f_ulong();
case TypeCode.Int16:
{
var func = (Func<short>)mInfo.CreateDelegate(typeof(Func<short>));
return () => func();
}
var f_short = (Func<short>) mInfo.CreateDelegate(typeof(Func<short>));
return () => f_short();
case TypeCode.Int32:
{
var f = (Func<int>)mInfo.CreateDelegate(typeof(Func<int>));
return () => f();
}
var f_int = (Func<int>) mInfo.CreateDelegate(typeof(Func<int>));
return () => f_int();
case TypeCode.Int64:
{
var f = (Func<long>)mInfo.CreateDelegate(typeof(Func<long>));
return () => f();
}
var f_long = (Func<long>) mInfo.CreateDelegate(typeof(Func<long>));
return () => f_long();
case TypeCode.Decimal:
{
var f = (Func<decimal>)mInfo.CreateDelegate(typeof(Func<decimal>));
return () => (double)f();
}
var f_decimal = (Func<decimal>) mInfo.CreateDelegate(typeof(Func<decimal>));
return () => (double) f_decimal();
case TypeCode.Double:
{
var f = (Func<double>)mInfo.CreateDelegate(typeof(Func<double>));
return f;
}
var f_double = (Func<double>) mInfo.CreateDelegate(typeof(Func<double>));
return f_double;
case TypeCode.Single:
{
var f = (Func<float>)mInfo.CreateDelegate(typeof(Func<float>));
return () => f();
}
var f_float = (Func<float>) mInfo.CreateDelegate(typeof(Func<float>));
return () => f_float();
default:
{
var message = $"Method '{mInfo.DeclaringType}.{mInfo.Name} ({mInfo.ReturnType})' is not supported.";
Debug.LogException(new Exception(message));
UnityEngine.Debug.LogException(new Exception(string.Format("Method '{1}.{0}' is not supported.", mInfo.Name, mInfo.DeclaringType)));
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;
}
public static void AppendFormatNoAlloc(this StringBuilder sb, string format, double arg0 = 0, double arg1 = 0,
double arg2 = 0, double arg3 = 0)
public static void AppendFormatNoAlloc(this StringBuilder sb, string format, double arg0 = 0, double arg1 = 0, double arg2 = 0, double arg3 = 0)
{
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);
var sign = number < 0;
@ -70,7 +69,7 @@ namespace Coffee.NanoMonitor
number /= 10;
sb.Append((char) (n + 48));
} while (1 <= number || sb.Length - startIndex < padding);
} while (1 <= number || (sb.Length - startIndex) < padding);
if (sign)
{
@ -83,12 +82,11 @@ namespace Coffee.NanoMonitor
sb.Alignment(alignment, startIndex, endIndex);
}
private static void AppendDouble(this StringBuilder sb, double number, int padding, int precision,
int alignment)
internal static void AppendDouble(this StringBuilder sb, double number, int padding, int precision, int alignment)
{
var integer = Math.Truncate(number);
var startIndex = sb.Length;
sb.AppendInteger(integer, padding, 0);
sb.AppendInteger(integer, padding, precision, 0);
if (0 < precision)
{
@ -108,18 +106,20 @@ namespace Coffee.NanoMonitor
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)
{
// swap
(sb[start], sb[end]) = (sb[end], sb[start]);
var c = sb[start];
sb[start] = sb[end];
sb[end] = c;
start++;
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;
@ -129,8 +129,9 @@ namespace Coffee.NanoMonitor
sb.Append(' ', alignment - len);
for (var i = 0; i < len; i++)
{
// swap
(sb[end - i], sb[start + alignment - i - 1]) = (sb[start + alignment - i - 1], sb[end - i]);
var c = sb[end - i];
sb[end - i] = sb[start + alignment - i - 1];
sb[start + alignment - i - 1] = c;
}
}
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,
out int alignment)
internal static int GetFormat(string format, int i, out int argIndex, out int padding, out int precision, out int alignment)
{
argIndex = -1;
padding = 0;

View File

@ -1,35 +1,26 @@
using UnityEngine;
using UnityEngine.Serialization;
namespace Coffee.UIExtensions.Demo
{
public class UIParticle_PerformanceDemo : MonoBehaviour
{
[FormerlySerializedAs("copyOrigin")]
[SerializeField]
private GameObject m_CopyOrigin;
[FormerlySerializedAs("copyCount")]
[SerializeField]
public int m_CopyCount;
[FormerlySerializedAs("root")]
[SerializeField]
public Canvas m_RootCanvas;
public GameObject copyOrigin;
public int copyCount;
public Canvas root;
private void Start()
{
Application.targetFrameRate = 60;
if (m_CopyOrigin)
if (copyOrigin)
{
m_CopyOrigin.SetActive(false);
copyOrigin.SetActive(false);
var parent = m_CopyOrigin.transform.parent;
for (var i = 0; i < m_CopyCount; i++)
var parent = copyOrigin.transform.parent;
for (var i = 0; i < copyCount; i++)
{
var go = Instantiate(m_CopyOrigin, parent, false);
go.name = string.Format("{0} {1}", m_CopyOrigin.name, i + 1);
var go = GameObject.Instantiate(copyOrigin, parent, false);
go.name = string.Format("{0} {1}", copyOrigin.name, i + 1);
go.hideFlags = HideFlags.DontSave;
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))
{
uip.enabled = flag;
if (Screen.width == 400 && Screen.height == 720)
Screen.SetResolution(720, 720, false);
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>())
{
@ -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
? UIParticle.MeshSharing.Auto
: UIParticle.MeshSharing.None;
uip.meshSharing = enabled ? 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
? 4
: 0;
uip.groupMaxId = enabled ? 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);
}
}
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_CustomReflection: {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
--- !u!157 &3
LightmapSettings:
@ -757,6 +757,85 @@ Transform:
m_Father: {fileID: 0}
m_RootOrder: 2
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
GameObject:
m_ObjectHideFlags: 0
@ -893,12 +972,12 @@ PrefabInstance:
- target: {fileID: 864895766138689324, guid: a4669dfbba654429bbcddf0c9b79fb8b,
type: 3}
propertyPath: m_AnchoredPosition.x
value: 240
value: 240.00002
objectReference: {fileID: 0}
- target: {fileID: 864895766138689324, guid: a4669dfbba654429bbcddf0c9b79fb8b,
type: 3}
propertyPath: m_AnchoredPosition.y
value: -180
value: -179.99998
objectReference: {fileID: 0}
- target: {fileID: 864895766138689324, guid: a4669dfbba654429bbcddf0c9b79fb8b,
type: 3}
@ -1081,85 +1160,6 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 608849065}
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
GameObject:
m_ObjectHideFlags: 0
@ -1287,9 +1287,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 3a595cf9cf93f4e0686f247a5e7e19ce, type: 3}
m_Name:
m_EditorClassIdentifier:
m_CopyOrigin: {fileID: 1581241706}
m_CopyCount: 266
m_RootCanvas: {fileID: 919637305}
copyOrigin: {fileID: 1581241706}
copyCount: 266
root: {fileID: 919637305}
--- !u!4 &903906985
Transform:
m_ObjectHideFlags: 0
@ -1505,6 +1505,11 @@ PrefabInstance:
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 151878254403844079, guid: 784696794bc6345bc80bf49623581c2e,
type: 3}
propertyPath: m_TargetGraphic
value:
objectReference: {fileID: 1002673091}
- target: {fileID: 323826319202443003, guid: 784696794bc6345bc80bf49623581c2e,
type: 3}
propertyPath: m_AnchorMin.y
@ -1525,6 +1530,11 @@ PrefabInstance:
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1087632561348207336, guid: 784696794bc6345bc80bf49623581c2e,
type: 3}
propertyPath: m_TargetGraphic
value:
objectReference: {fileID: 1002673092}
- target: {fileID: 1458366075964155596, guid: 784696794bc6345bc80bf49623581c2e,
type: 3}
propertyPath: m_AnchorMin.y
@ -1554,7 +1564,7 @@ PrefabInstance:
type: 3}
propertyPath: m_CustomMonitorItems.Array.data[1].m_Text
value:
objectReference: {fileID: 733597320}
objectReference: {fileID: 455490118}
- target: {fileID: 4567906826058368312, guid: 784696794bc6345bc80bf49623581c2e,
type: 3}
propertyPath: m_CustomMonitorItems.Array.data[1].m_Format
@ -1620,6 +1630,16 @@ PrefabInstance:
propertyPath: m_SizeDelta.x
value: 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,
type: 3}
propertyPath: m_SizeDelta.y
@ -1783,6 +1803,54 @@ RectTransform:
type: 3}
m_PrefabInstance: {fileID: 1002673087}
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
GameObject:
m_ObjectHideFlags: 0
@ -3564,11 +3632,6 @@ PrefabInstance:
m_Modification:
m_TransformParent: {fileID: 1924051224}
m_Modifications:
- target: {fileID: 864895764861262036, guid: a4669dfbba654429bbcddf0c9b79fb8b,
type: 3}
propertyPath: m_IgnoreCanvasScaler
value: 1
objectReference: {fileID: 0}
- target: {fileID: 864895765507486433, guid: a4669dfbba654429bbcddf0c9b79fb8b,
type: 3}
propertyPath: m_AnchorMax.x
@ -3727,17 +3790,17 @@ PrefabInstance:
- target: {fileID: 1088568576793901561, guid: a4669dfbba654429bbcddf0c9b79fb8b,
type: 3}
propertyPath: m_LocalScale.x
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1088568576793901561, guid: a4669dfbba654429bbcddf0c9b79fb8b,
type: 3}
propertyPath: m_LocalScale.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1088568576793901561, guid: a4669dfbba654429bbcddf0c9b79fb8b,
type: 3}
propertyPath: m_LocalScale.z
value: 1
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: a4669dfbba654429bbcddf0c9b79fb8b, type: 3}

View File

@ -1,9 +1,8 @@
using System;
using UnityEngine;
using UnityEngine;
namespace Coffee.UIExtensions
{
[Serializable]
[System.Serializable]
public class AnimatableProperty : ISerializationCallbackReceiver
{
public enum ShaderPropertyType
@ -12,11 +11,11 @@ namespace Coffee.UIExtensions
Vector,
Float,
Range,
Texture
Texture,
}
[SerializeField] private string m_Name = "";
[SerializeField] private ShaderPropertyType m_Type = ShaderPropertyType.Vector;
[SerializeField] string m_Name = "";
[SerializeField] ShaderPropertyType m_Type = ShaderPropertyType.Vector;
public int id { get; private set; }
public ShaderPropertyType type
@ -24,15 +23,6 @@ namespace Coffee.UIExtensions
get { return m_Type; }
}
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
}
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
id = Shader.PropertyToID(m_Name);
}
public void UpdateMaterialProperties(Material material, MaterialPropertyBlock mpb)
{
if (!material.HasProperty(id)) return;
@ -41,38 +31,35 @@ namespace Coffee.UIExtensions
{
case ShaderPropertyType.Color:
var color = mpb.GetColor(id);
if (color != default)
{
if (color != default(Color))
material.SetColor(id, color);
}
break;
case ShaderPropertyType.Vector:
var vector = mpb.GetVector(id);
if (vector != default)
{
if (vector != default(Vector4))
material.SetVector(id, vector);
}
break;
case ShaderPropertyType.Float:
case ShaderPropertyType.Range:
var value = mpb.GetFloat(id);
if (!Mathf.Approximately(value, 0))
{
if (value != default(float))
material.SetFloat(id, value);
}
break;
case ShaderPropertyType.Texture:
var tex = mpb.GetTexture(id);
if (tex != default(Texture))
{
material.SetTexture(id, tex);
}
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)
{
var jsonPath = AssetDatabase.GUIDToAssetPath(jsonGuid);
if (string.IsNullOrEmpty(jsonPath)) return;
var jsonDir = Path.GetDirectoryName(jsonPath);
if (string.IsNullOrEmpty(jsonDir)) return;
var packageRoot = jsonDir.Replace('\\', '/');
var packageRoot = Path.GetDirectoryName(jsonPath).Replace('\\', '/');
var json = File.ReadAllText(jsonPath);
var version = Regex.Match(json, "\"version\"\\s*:\\s*\"([^\"]+)\"").Groups[1].Value;
var src = $"{packageRoot}/Samples~/{sampleName}";
var dst = $"Assets/Samples/{k_DisplayName}/{version}/{sampleName}";
var src = string.Format("{0}/Samples~/{1}", packageRoot, sampleName);
var dst = string.Format("Assets/Samples/{0}/{1}/{2}", k_DisplayName, version, sampleName);
var previousPath = GetPreviousSamplePath(k_DisplayName, sampleName);
// Remove the previous sample directory.
@ -51,45 +46,33 @@ namespace Coffee.UIExtensions
+ previousPath
+ "\n\nIt will be deleted when you update. Are you sure you want to continue?";
if (!EditorUtility.DisplayDialog("Sample Importer", msg, "OK", "Cancel"))
{
return;
}
FileUtil.DeleteFileOrDirectory(previousPath);
var metaFile = previousPath + ".meta";
if (File.Exists(metaFile))
{
FileUtil.DeleteFileOrDirectory(metaFile);
}
}
if (!Directory.Exists(dst))
{
FileUtil.DeleteFileOrDirectory(dst);
}
var dstDir = Path.GetDirectoryName(dst);
if (!string.IsNullOrEmpty(dstDir) && !Directory.Exists(dstDir))
{
if (!Directory.Exists(dstDir))
Directory.CreateDirectory(dstDir);
}
if (Directory.Exists(src))
{
FileUtil.CopyFileOrDirectory(src, dst);
}
else
{
throw new DirectoryNotFoundException(src);
}
AssetDatabase.Refresh(ImportAssetOptions.ImportRecursive);
}
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);
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
using UnityEditor.Overlays;
#else
using System.Reflection;
#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
{
@ -28,10 +23,7 @@ namespace Coffee.UIExtensions
{
#if UNITY_2021_2_OR_NEWER
#if UNITY_2022_1_OR_NEWER
[Overlay(typeof(SceneView), "Scene View/UI Particles", "UI Particles", true,
defaultDockPosition = DockPosition.Bottom,
defaultDockZone = DockZone.Floating,
defaultLayout = Layout.Panel)]
[Overlay(typeof(SceneView), "Scene View/UI Particles", "UI Particles", true, defaultDockPosition = DockPosition.Bottom, defaultDockZone = DockZone.Floating, defaultLayout = Layout.Panel)]
#else
[Overlay(typeof(SceneView), "Scene View/UI Particles", "UI Particles", true)]
#endif
@ -61,22 +53,24 @@ namespace Coffee.UIExtensions
private static readonly GUIContent s_ContentRandom = new GUIContent("Random");
private static readonly GUIContent s_ContentScale = new GUIContent("Scale");
private static SerializedObject s_SerializedObject;
private static bool s_XYZMode;
private SerializedProperty _maskable;
private SerializedProperty _scale3D;
private SerializedProperty _animatableProperties;
private SerializedProperty _meshSharing;
private SerializedProperty _groupId;
private SerializedProperty _groupMaxId;
private SerializedProperty _positionMode;
private SerializedProperty _autoScaling;
#if !SERIALIZE_FIELD_MASKABLE
private SerializedProperty m_Maskable;
#endif
private SerializedProperty m_Scale3D;
private SerializedProperty m_AnimatableProperties;
private SerializedProperty m_MeshSharing;
private SerializedProperty m_GroupId;
private SerializedProperty m_GroupMaxId;
private SerializedProperty m_AbsoluteMode;
private ReorderableList _ro;
static private bool _xyzMode;
private bool _showMax;
private static readonly HashSet<Shader> s_Shaders = new HashSet<Shader>();
private static readonly List<ParticleSystemVertexStream> s_Streams = new List<ParticleSystemVertexStream>();
private static readonly List<string> s_MaskablePropertyNames = new List<string>
{
"_Stencil",
@ -84,20 +78,21 @@ namespace Coffee.UIExtensions
"_StencilOp",
"_StencilWriteMask",
"_StencilReadMask",
"_ColorMask"
"_ColorMask",
};
[InitializeOnLoadMethod]
private static void Init()
static void Init()
{
#if !UNITY_2021_2_OR_NEWER
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);
var windowFunction = (Action<Object, SceneView>)WindowFunction;
var windowFunction = (Action<UnityEngine.Object, SceneView>)WindowFunction;
var windowFunctionType = Type.GetType("UnityEditor.SceneViewOverlay+WindowFunction, UnityEditor");
var windowFunctionDelegate = Delegate.CreateDelegate(windowFunctionType, windowFunction.Method);
var windowTitle = new GUIContent(ObjectNames.NicifyVariableName(nameof(UIParticle)));
var windowTitle = new GUIContent(ObjectNames.NicifyVariableName(typeof(UIParticle).Name));
#if UNITY_2019_2_OR_NEWER
//public static void Window(GUIContent title, WindowFunction sceneViewFunc, int order, Object target, WindowDisplayOption option, EditorWindow window = null)
var sceneViewArgs = new object[] { windowTitle, windowFunctionDelegate, 599, null, 2, null };
@ -107,7 +102,7 @@ namespace Coffee.UIExtensions
#endif
#if UNITY_2019_1_OR_NEWER
SceneView.duringSceneGui += _ =>
SceneView.duringSceneGui += _ => { if (s_SerializedObject != null) miSceneViewOverlayWindow.Invoke(null, sceneViewArgs); };
#else
SceneView.onSceneGUIDelegate += _ =>
#endif
@ -119,21 +114,25 @@ namespace Coffee.UIExtensions
};
#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)
.Select(x => x.GetComponentInParent<UIParticle>(true))
.Select(x => x.GetComponentInParent<UIParticle>())
.Where(x => x && x.canvas)
.Concat(Selection.gameObjects.Select(x => x.GetComponent<UIParticle>())
.Where(x => x && x.canvas))
.Concat(
Selection.gameObjects
.Select(x => x.GetComponent<UIParticle>())
.Where(x => x && x.canvas)
)
.Distinct()
.ToArray();
return 0 < uiParticles.Length ? new SerializedObject(uiParticles) : null;
}
};
s_SerializedObject = CreateSerializeObject();
Selection.selectionChanged += () => s_SerializedObject = CreateSerializeObject();
s_SerializedObject = createSerializeObject();
Selection.selectionChanged += () => s_SerializedObject = createSerializeObject();
}
//################################
@ -146,21 +145,19 @@ namespace Coffee.UIExtensions
{
base.OnEnable();
_maskable = serializedObject.FindProperty("m_Maskable");
_scale3D = serializedObject.FindProperty("m_Scale3D");
_animatableProperties = serializedObject.FindProperty("m_AnimatableProperties");
_meshSharing = serializedObject.FindProperty("m_MeshSharing");
_groupId = serializedObject.FindProperty("m_GroupId");
_groupMaxId = serializedObject.FindProperty("m_GroupMaxId");
_positionMode = serializedObject.FindProperty("m_PositionMode");
_autoScaling = serializedObject.FindProperty("m_AutoScaling");
m_Maskable = serializedObject.FindProperty("m_Maskable");
m_Scale3D = serializedObject.FindProperty("m_Scale3D");
m_AnimatableProperties = serializedObject.FindProperty("m_AnimatableProperties");
m_MeshSharing = serializedObject.FindProperty("m_MeshSharing");
m_GroupId = serializedObject.FindProperty("m_GroupId");
m_GroupMaxId = serializedObject.FindProperty("m_GroupMaxId");
m_AbsoluteMode = serializedObject.FindProperty("m_AbsoluteMode");
var sp = serializedObject.FindProperty("m_Particles");
_ro = new ReorderableList(sp.serializedObject, sp, true, true, true, true)
{
elementHeight = EditorGUIUtility.singleLineHeight * 3 + 4,
elementHeightCallback = _ => 3 * (EditorGUIUtility.singleLineHeight + 2),
drawElementCallback = (rect, index, _, __) =>
_ro = new ReorderableList(sp.serializedObject, sp, true, true, true, true);
_ro.elementHeight = (EditorGUIUtility.singleLineHeight * 3) + 4;
_ro.elementHeightCallback = _ => 3 * (EditorGUIUtility.singleLineHeight + 2);
_ro.drawElementCallback = (rect, index, _, __) =>
{
EditorGUI.BeginDisabledGroup(sp.hasMultipleDifferentValues);
rect.y += 1;
@ -182,35 +179,29 @@ namespace Coffee.UIExtensions
{
materials.serializedObject.ApplyModifiedProperties();
}
},
drawHeaderCallback = rect =>
};
_ro.drawHeaderCallback = rect =>
{
#if !UNITY_2019_3_OR_NEWER
rect.y -= 1;
#endif
var pos = new Rect(rect.x, rect.y, 150, rect.height);
EditorGUI.LabelField(pos, s_ContentRenderingOrder);
EditorGUI.LabelField(new Rect(rect.x, rect.y, 150, rect.height), s_ContentRenderingOrder);
pos = new Rect(rect.width - 35, rect.y, 60, rect.height);
if (GUI.Button(pos, s_ContentRefresh, EditorStyles.miniButton))
if (GUI.Button(new Rect(rect.width - 35, rect.y, 60, rect.height), s_ContentRefresh, EditorStyles.miniButton))
{
foreach (var uip in targets.OfType<UIParticle>())
foreach (UIParticle t in targets)
{
uip.RefreshParticles();
EditorUtility.SetDirty(uip);
}
t.RefreshParticles();
EditorUtility.SetDirty(t);
}
}
};
// On select UIParticle, refresh particles.
if (!Application.isPlaying)
foreach (UIParticle t in targets)
{
foreach (var uip in targets.OfType<UIParticle>())
{
if (PrefabUtility.GetPrefabAssetType(uip) != PrefabAssetType.NotAPrefab) continue;
uip.RefreshParticles(uip.particles);
}
if (Application.isPlaying || PrefabUtility.GetPrefabAssetType(t) != PrefabAssetType.NotAPrefab) continue;
t.RefreshParticles(t.particles);
}
}
@ -239,11 +230,11 @@ namespace Coffee.UIExtensions
serializedObject.Update();
// Maskable
EditorGUILayout.PropertyField(_maskable);
EditorGUILayout.PropertyField(m_Maskable);
// Scale
EditorGUI.BeginDisabledGroup(!_meshSharing.hasMultipleDifferentValues && _meshSharing.intValue == 4);
s_XYZMode = DrawFloatOrVector3Field(_scale3D, s_XYZMode);
EditorGUI.BeginDisabledGroup(!m_MeshSharing.hasMultipleDifferentValues && m_MeshSharing.intValue == 4);
_xyzMode = DrawFloatOrVector3Field(m_Scale3D, _xyzMode);
EditorGUI.EndDisabledGroup();
// AnimatableProperties
@ -253,11 +244,12 @@ namespace Coffee.UIExtensions
.Where(x => x)
.ToArray();
AnimatablePropertyEditor.Draw(_animatableProperties, mats);
// Animated properties
AnimatedPropertiesEditor.DrawAnimatableProperties(m_AnimatableProperties, mats);
// Mesh sharing
EditorGUI.BeginChangeCheck();
_showMax = DrawMeshSharing(_meshSharing, _groupId, _groupMaxId, _showMax);
_showMax = DrawMeshSharing(m_MeshSharing, m_GroupId, m_GroupMaxId, _showMax);
if (EditorGUI.EndChangeCheck())
{
serializedObject.ApplyModifiedProperties();
@ -267,11 +259,8 @@ namespace Coffee.UIExtensions
}
}
// Position Mode
EditorGUILayout.PropertyField(_positionMode);
// Auto Scaling
DrawAutoScaling(_autoScaling, targets.OfType<UIParticle>());
// Absolute Mode
EditorGUILayout.PropertyField(m_AbsoluteMode);
// Target ParticleSystems.
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?
if (current.maskable && current.GetComponentInParent<Mask>(false))
if (current.maskable && current.GetComponentInParent<Mask>())
{
foreach (var mat in current.materials)
{
@ -314,20 +289,15 @@ namespace Coffee.UIExtensions
{
if (mat.HasProperty(propName)) continue;
EditorGUILayout.HelpBox(
$"Shader '{shader.name}' doesn't have '{propName}' property." +
"\nThis graphic cannot be masked.",
MessageType.Warning);
EditorGUILayout.HelpBox(string.Format("Shader '{0}' doesn't have '{1}' property. This graphic cannot be masked.", shader.name, propName), MessageType.Warning);
break;
}
}
}
s_Shaders.Clear();
// 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, label))
if (FixButton(current.m_IsTrail, "This UIParticle component should be removed. The UIParticle for trails is no longer needed."))
{
DestroyUIParticle(current);
}
@ -343,9 +313,7 @@ namespace Coffee.UIExtensions
{
var so = new SerializedObject(allPsRenderers);
var sp = so.FindProperty("m_ApplyActiveColorSpace");
label = "When using linear color space, the particle colors are not output correctly.\n" +
"To fix, set 'Apply Active Color Space' in renderer module to false.";
if (FixButton(sp.boolValue || sp.hasMultipleDifferentValues, label))
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."))
{
sp.boolValue = false;
so.ApplyModifiedProperties();
@ -360,21 +328,10 @@ namespace Coffee.UIExtensions
if (2 < s_Streams.Select(GetUsedComponentCount).Sum())
{
EditorGUILayout.HelpBox(
$"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();
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);
}
}
}
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)
@ -432,12 +389,10 @@ namespace Coffee.UIExtensions
case ParticleSystemVertexStream.Custom2XYZW:
return 4;
}
return 3;
}
private static bool DrawMeshSharing(SerializedProperty spMeshSharing, SerializedProperty spGroupId,
SerializedProperty spGroupMaxId, bool showMax)
private static bool DrawMeshSharing(SerializedProperty spMeshSharing, SerializedProperty spGroupId, SerializedProperty spGroupMaxId, bool showMax)
{
showMax |= spGroupId.intValue != spGroupMaxId.intValue ||
spGroupId.hasMultipleDifferentValues ||
@ -449,10 +404,7 @@ namespace Coffee.UIExtensions
EditorGUI.BeginChangeCheck();
showMax = GUILayout.Toggle(showMax, s_ContentRandom, EditorStyles.miniButton, GUILayout.Width(60));
if (EditorGUI.EndChangeCheck() && !showMax)
{
spGroupMaxId.intValue = spGroupId.intValue;
}
EditorGUILayout.EndHorizontal();
EditorGUI.BeginDisabledGroup(spMeshSharing.intValue == 0);
@ -465,41 +417,20 @@ namespace Coffee.UIExtensions
else if (spMeshSharing.intValue == 1 || spMeshSharing.intValue == 4)
{
EditorGUI.BeginDisabledGroup(true);
var obj = UIParticleUpdater.GetPrimary(spGroupId.intValue);
EditorGUILayout.ObjectField("Primary", obj, typeof(UIParticle), false);
EditorGUILayout.ObjectField("Primary", UIParticleUpdater.GetPrimary(spGroupId.intValue), typeof(UIParticle), false);
EditorGUI.EndDisabledGroup();
}
EditorGUI.indentLevel--;
EditorGUI.EndDisabledGroup();
return showMax;
}
private static void DrawAutoScaling(SerializedProperty prop, IEnumerable<UIParticle> uiParticles)
{
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)
private static void WindowFunction(UnityEngine.Object target, SceneView sceneView)
{
try
{
if (s_SerializedObject == null || !s_SerializedObject.targetObject) return;
var uiParticles = s_SerializedObject.targetObjects.OfType<UIParticle>();
if (uiParticles.Any(x => !x || !x.canvas)) return;
if (s_SerializedObject.targetObjects.OfType<UIParticle>().Any(x => !x || !x.canvas)) return;
s_SerializedObject.Update();
using (new EditorGUILayout.VerticalScope(GUILayout.Width(220f)))
@ -507,36 +438,37 @@ namespace Coffee.UIExtensions
var labelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 100;
EditorGUILayout.PropertyField(s_SerializedObject.FindProperty("m_Enabled"));
s_XYZMode = DrawFloatOrVector3Field(s_SerializedObject.FindProperty("m_Scale3D"), s_XYZMode);
EditorGUILayout.PropertyField(s_SerializedObject.FindProperty("m_PositionMode"));
DrawAutoScaling(s_SerializedObject.FindProperty("m_AutoScaling"), uiParticles);
_xyzMode = DrawFloatOrVector3Field(s_SerializedObject.FindProperty("m_Scale3D"), _xyzMode);
EditorGUILayout.PropertyField(s_SerializedObject.FindProperty("m_AbsoluteMode"));
EditorGUIUtility.labelWidth = labelWidth;
}
s_SerializedObject.ApplyModifiedProperties();
}
catch
{
// ignored
}
}
private void DestroyUIParticle(UIParticle p, bool ignoreCurrent = false)
{
if (!p || (ignoreCurrent && target == p)) return;
if (!p || ignoreCurrent && target == p) return;
var cr = p.canvasRenderer;
DestroyImmediate(p);
DestroyImmediate(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
var stage = PrefabStageUtility.GetCurrentPrefabStage();
if (stage != null && stage.scene.isLoaded)
{
#if UNITY_2020_1_OR_NEWER
string prefabAssetPath = stage.assetPath;
#else
var prefabAssetPath = stage.prefabAssetPath;
string prefabAssetPath = stage.prefabAssetPath;
#endif
PrefabUtility.SaveAsPrefabAsset(stage.prefabContentsRoot, prefabAssetPath);
}
@ -585,10 +517,7 @@ namespace Coffee.UIExtensions
EditorGUI.BeginChangeCheck();
showXyz = GUILayout.Toggle(showXyz, s_Content3D, EditorStyles.miniButton, GUILayout.Width(30));
if (EditorGUI.EndChangeCheck() && !showXyz)
{
z.floatValue = y.floatValue = x.floatValue;
}
EditorGUILayout.EndHorizontal();
return showXyz;

View File

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

View File

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

View File

@ -2,11 +2,11 @@
#define SERIALIZE_FIELD_MASKABLE
#endif
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Coffee.UIParticleExtensions;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Serialization;
using UnityEngine.UI;
[assembly: InternalsVisibleTo("Coffee.UIParticle.Editor")]
@ -19,7 +19,7 @@ namespace Coffee.UIExtensions
[ExecuteAlways]
[RequireComponent(typeof(RectTransform))]
[RequireComponent(typeof(CanvasRenderer))]
public class UIParticle : MaskableGraphic, ISerializationCallbackReceiver
public class UIParticle : MaskableGraphic
{
public enum MeshSharing
{
@ -27,34 +27,16 @@ namespace Coffee.UIExtensions
Auto,
Primary,
PrimarySimulator,
Replica
Reprica,
}
public enum PositionMode
{
Relative,
Absolute
}
[HideInInspector]
[SerializeField]
internal bool m_IsTrail;
[HideInInspector]
[FormerlySerializedAs("m_IgnoreParent")]
[SerializeField]
private bool m_IgnoreCanvasScaler;
[HideInInspector]
[SerializeField]
private bool m_AbsoluteMode;
[HideInInspector][SerializeField] internal bool m_IsTrail = false;
[Tooltip("Particle effect scale")]
[SerializeField]
private Vector3 m_Scale3D = new Vector3(10, 10, 10);
[Tooltip("Animatable material properties.\n" +
"If you want to change the material properties of the ParticleSystem in Animation, enable it.")]
[Tooltip("Animatable material properties. If you want to change the material properties of the ParticleSystem in Animation, enable it.")]
[SerializeField]
internal AnimatableProperty[] m_AnimatableProperties = new AnimatableProperty[0];
@ -62,44 +44,33 @@ namespace Coffee.UIExtensions
[SerializeField]
private List<ParticleSystem> m_Particles = new List<ParticleSystem>();
[Tooltip("Mesh sharing.\n" +
"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.")]
[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.")]
[SerializeField]
private MeshSharing m_MeshSharing = MeshSharing.None;
[Tooltip("Mesh sharing group ID.\n" +
"If non-zero is specified, particle simulation results are shared within the group.")]
[Tooltip("Mesh sharing group ID. If non-zero is specified, particle simulation results are shared within the group.")]
[SerializeField]
private int m_GroupId;
private int m_GroupId = 0;
[SerializeField]
private int m_GroupMaxId;
[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;
private int m_GroupMaxId = 0;
[SerializeField]
[Tooltip("Transform.lossyScale (=world scale) is automatically set to (1, 1, 1), " +
"to prevent the root-Canvas scale from affecting the hierarchy-scaled ParticleSystem.")]
private bool m_AutoScaling = true;
[Tooltip("The particles will be emitted at the ParticleSystem position.\nMove the UIParticle/ParticleSystem to move the particle.")]
private bool m_AbsoluteMode = false;
private List<UIParticleRenderer> m_Renderers = new List<UIParticleRenderer>();
#if !SERIALIZE_FIELD_MASKABLE
[SerializeField]
private bool m_Maskable = true;
[SerializeField] private bool m_Maskable = true;
#endif
private readonly List<UIParticleRenderer> _renderers = new List<UIParticleRenderer>();
private int _groupId;
private Camera _orthoCamera;
private DrivenRectTransformTracker _tracker;
private Camera _orthoCamera;
private int _groupId;
/// <summary>
/// Should this graphic be considered a target for ray-casting?
/// Should this graphic be considered a target for raycasting?
/// </summary>
public override bool raycastTarget
{
@ -108,12 +79,11 @@ namespace Coffee.UIExtensions
}
/// <summary>
/// Mesh sharing.
/// None: disable mesh sharing.
/// Auto: automatically select Primary/Replica.
/// Mesh sharing.None: disable mesh sharing.
/// Auto: automatically select Primary/Reprica.
/// Primary: provides particle simulation results to the same group.
/// 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>
public MeshSharing meshSharing
{
@ -122,8 +92,7 @@ namespace Coffee.UIExtensions
}
/// <summary>
/// Mesh sharing group ID.
/// If non-zero is specified, particle simulation results are shared within the group.
/// Mesh sharing group ID. If non-zero is specified, particle simulation results are shared within the group.
/// </summary>
public int groupId
{
@ -133,11 +102,9 @@ namespace Coffee.UIExtensions
if (m_GroupId == value) return;
m_GroupId = value;
if (m_GroupId != m_GroupMaxId)
{
ResetGroupId();
}
}
}
public int groupMaxId
{
@ -151,40 +118,14 @@ namespace Coffee.UIExtensions
}
/// <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>
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.
/// Absolute particle position mode.
/// The particles will be emitted at the ParticleSystem position.
/// Move the UIParticle/ParticleSystem to move the particle.
/// </summary>
public bool absoluteMode
{
get { return m_PositionMode == PositionMode.Absolute; }
set { positionMode = value ? PositionMode.Absolute : PositionMode.Relative; }
}
/// <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();
}
get { return m_AbsoluteMode; }
set { m_AbsoluteMode = value; }
}
internal bool useMeshSharing
@ -194,33 +135,17 @@ namespace Coffee.UIExtensions
internal bool isPrimary
{
get
{
return m_MeshSharing == MeshSharing.Primary
|| m_MeshSharing == MeshSharing.PrimarySimulator;
}
get { return m_MeshSharing == MeshSharing.Primary || m_MeshSharing == MeshSharing.PrimarySimulator; }
}
internal bool canSimulate
{
get
{
return m_MeshSharing == MeshSharing.None
|| m_MeshSharing == MeshSharing.Auto
|| m_MeshSharing == MeshSharing.Primary
|| m_MeshSharing == MeshSharing.PrimarySimulator;
}
get { return m_MeshSharing == MeshSharing.None || m_MeshSharing == MeshSharing.Auto || m_MeshSharing == MeshSharing.Primary || m_MeshSharing == MeshSharing.PrimarySimulator; }
}
internal bool canRender
{
get
{
return m_MeshSharing == MeshSharing.None
|| m_MeshSharing == MeshSharing.Auto
|| m_MeshSharing == MeshSharing.Primary
|| m_MeshSharing == MeshSharing.Replica;
}
get { return m_MeshSharing == MeshSharing.None || m_MeshSharing == MeshSharing.Auto || m_MeshSharing == MeshSharing.Primary || m_MeshSharing == MeshSharing.Reprica; }
}
/// <summary>
@ -253,12 +178,12 @@ namespace Coffee.UIExtensions
{
get
{
for (var i = 0; i < _renderers.Count; i++)
for (var i = 0; i < m_Renderers.Count; i++)
{
var r = _renderers[i];
if (!r || !r.material) continue;
yield return r.material;
if (!m_Renderers[i] || !m_Renderers[i].material) continue;
yield return m_Renderers[i].material;
}
yield break;
}
}
@ -270,78 +195,7 @@ namespace Coffee.UIExtensions
/// <summary>
/// Paused.
/// </summary>
public bool isPaused { get; private 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 bool isPaused { get; internal set; }
public void Play()
{
@ -366,24 +220,6 @@ namespace Coffee.UIExtensions
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()
{
particles.Exec(p => p.Clear());
@ -403,10 +239,14 @@ namespace Coffee.UIExtensions
{
var go = child.gameObject;
go.SetActive(false);
if (destroyOldParticles)
{
Misc.Destroy(go);
}
if (!destroyOldParticles) continue;
#if UNITY_EDITOR
if (!Application.isPlaying)
DestroyImmediate(go);
else
#endif
Destroy(go);
}
var tr = instance.transform;
@ -431,60 +271,48 @@ namespace Coffee.UIExtensions
private void RefreshParticles(GameObject root)
{
if (!root) return;
root.GetComponentsInChildren(true, particles);
particles.RemoveAll(x => x.GetComponentInParent<UIParticle>(true) != this);
root.GetComponentsInChildren(particles);
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;
if (tsa.mode == ParticleSystemAnimationMode.Sprites && tsa.uvChannelMask == 0)
{
tsa.uvChannelMask = UVChannelFlags.UV0;
}
}
RefreshParticles(particles);
}
public void RefreshParticles(List<ParticleSystem> particles)
{
// #246: Nullptr exceptions when using nested UIParticle components in hierarchy
_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);
}
GetComponentsInChildren(m_Renderers);
var j = 0;
for (var i = 0; i < particles.Count; i++)
{
var ps = particles[i];
if (!ps) continue;
GetRenderer(j++).Set(this, ps, false);
if (ps.trails.enabled)
if (!particles[i]) continue;
GetRenderer(j++).Set(this, particles[i], false);
if (particles[i].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()
{
parentScale = transform.parent.lossyScale;
if (!autoScaling) return;
var newScale = parentScale.Inverse();
//var newScale = Vector3.one;
//if (uiScaling)
//{
// newScale = transform.parent.lossyScale.Inverse();
//}
var newScale = transform.parent.lossyScale.Inverse();
if (transform.localScale != newScale)
{
transform.localScale = newScale;
@ -495,40 +323,73 @@ namespace Coffee.UIExtensions
{
if (!isActiveAndEnabled) return;
for (var i = 0; i < _renderers.Count; i++)
{
var r = _renderers[i];
if (!r)
if (m_Renderers.Any(x => !x))
{
RefreshParticles(particles);
break;
}
}
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 (!r) continue;
r.UpdateMesh(bakeCamera);
if (!m_Renderers[i]) continue;
m_Renderers[i].UpdateMesh(bakeCamera);
}
}
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 (!r) continue;
r.UpdateParticleCount();
if (!m_Renderers[i]) continue;
m_Renderers[i].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()
{
_groupId = m_GroupId == m_GroupMaxId
? m_GroupId
: Random.Range(m_GroupId, m_GroupMaxId + 1);
if (m_GroupId == m_GroupMaxId)
{
_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()
@ -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()
{
for (var i = 0; i < _renderers.Count; i++)
for (var i = 0; i < m_Renderers.Count; i++)
{
var r = _renderers[i];
if (!r) continue;
r.maskable = maskable;
r.SetMaterialDirty();
if (!m_Renderers[i]) continue;
m_Renderers[i].maskable = maskable;
m_Renderers[i].SetMaterialDirty();
}
}
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 (!_renderers[index])
if (!m_Renderers[index])
{
_renderers[index] = UIParticleRenderer.AddRenderer(this, index);
m_Renderers[index] = UIParticleRenderer.AddRenderer(this, index);
}
return _renderers[index];
return m_Renderers[index];
}
private Camera GetBakeCamera()
{
if (!canvas) return Camera.main;
// When render mode is ScreenSpaceCamera or WorldSpace, use world camera.
// World camera.
var root = canvas.rootCanvas;
if (root.renderMode != RenderMode.ScreenSpaceOverlay)
{
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;
}
}
if (root.renderMode != RenderMode.ScreenSpaceOverlay) return root.worldCamera ? root.worldCamera : Camera.main;
// Create ortho-camera.
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.transform.SetParent(transform, false);
_orthoCamera = go.AddComponent<Camera>();
@ -613,17 +467,5 @@ namespace Coffee.UIExtensions
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 UnityEngine;
using UnityEngine.Events;
using System;
namespace Coffee.UIExtensions
{
@ -12,13 +12,7 @@ namespace Coffee.UIExtensions
{
Linear,
Smooth,
Sphere
}
public enum UpdateMode
{
Normal,
UnscaledTime
Sphere,
}
[SerializeField]
@ -30,7 +24,7 @@ namespace Coffee.UIExtensions
[Range(0f, 0.95f)]
[SerializeField]
private float m_DelayRate;
private float m_DelayRate = 0;
[Range(0.001f, 100f)]
[SerializeField]
@ -39,79 +33,68 @@ namespace Coffee.UIExtensions
[SerializeField]
private Movement m_Movement;
[SerializeField]
private UpdateMode m_UpdateMode;
[SerializeField]
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
{
get { return m_DelayRate; }
set { m_DelayRate = value; }
get
{
return m_DelayRate;
}
set
{
m_DelayRate = value;
}
}
public float maxSpeed
{
get { return m_MaxSpeed; }
set { m_MaxSpeed = value; }
get
{
return m_MaxSpeed;
}
set
{
m_MaxSpeed = value;
}
}
public Movement movement
{
get { return m_Movement; }
set { m_Movement = value; }
}
public UpdateMode updateMode
get
{
get { return m_UpdateMode; }
set { m_UpdateMode = value; }
return m_Movement;
}
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
{
m_ParticleSystem = value;
ApplyParticleSystem();
m_Movement = value;
}
}
private UIParticle _uiParticle;
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);
}
private void OnDisable()
{
UIParticleUpdater.Unregister(this);
}
private void OnDestroy()
{
_uiParticle = null;
m_ParticleSystem = null;
UIParticleUpdater.Unregister(this);
}
internal void Attract()
@ -145,7 +128,6 @@ namespace Coffee.UIExtensions
Debug.LogException(e);
}
}
continue;
}
@ -172,51 +154,36 @@ namespace Coffee.UIExtensions
var psPos = m_ParticleSystem.transform.position;
var attractorPos = transform.position;
var dstPos = attractorPos;
var isLocalSpace = m_ParticleSystem.IsLocalSpace();
if (isLocalSpace)
if (m_ParticleSystem.main.simulationSpace == ParticleSystemSimulationSpace.Local)
{
dstPos = m_ParticleSystem.transform.InverseTransformPoint(dstPos);
}
if (isUI)
{
var inverseScale = _uiParticle.parentScale.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;
dstPos = dstPos.GetScaled(_uiParticle.transform.localScale, _uiParticle.scale3D.Inverse());
}
#if UNITY_EDITOR
if (!Application.isPlaying && !isLocalSpace)
}
else
{
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
if (isUI)
{
dstPos.Scale(_uiParticle.transform.localScale);
dstPos.Scale(_uiParticle.scale3D.Inverse());
}
}
return dstPos;
}
private Vector3 GetAttractedPosition(Vector3 current, Vector3 target, float duration, float time)
{
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)
{
case Movement.Linear:
@ -233,26 +200,5 @@ namespace Coffee.UIExtensions
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 System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Coffee.UIParticleExtensions;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.Rendering;
using UnityEngine.UI;
using System.Collections.Generic;
namespace Coffee.UIExtensions
{
@ -14,34 +13,41 @@ namespace Coffee.UIExtensions
[AddComponentMenu("")]
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 MaterialPropertyBlock s_Mpb;
private static readonly List<UIParticleRenderer> s_Renderers = new List<UIParticleRenderer>();
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 bool _isTrail;
private Bounds _lastBounds;
private Material _modifiedMaterial;
private UIParticle _parent;
private ParticleSystem _particleSystem;
private int _prevParticleCount;
private Vector3 _prevPsPos;
private Vector3 _prevScale;
private Vector3 _prevPsPos;
private Vector2Int _prevScreenSize;
private bool _prewarm;
private ParticleSystemRenderer _renderer;
private bool _delay = false;
private bool _prewarm = false;
private Material _currentMaterialForRendering;
private Bounds _lastBounds;
public override Texture mainTexture
{
get { return _isTrail ? null : _particleSystem.GetTextureForSprite(); }
get
{
return _isTrail ? null : _particleSystem.GetTextureForSprite();
}
}
public override bool raycastTarget
{
get { return false; }
get
{
return false;
}
}
private Rect rootCanvasRect
@ -56,103 +62,32 @@ namespace Coffee.UIExtensions
{
var worldToLocalMatrix = canvas.rootCanvas.transform.worldToLocalMatrix;
for (var i = 0; i < 4; ++i)
{
s_Corners[i] = worldToLocalMatrix.MultiplyPoint(s_Corners[i]);
}
}
var corner1 = (Vector2) s_Corners[0];
var corner2 = (Vector2) s_Corners[0];
for (var i = 1; i < 4; ++i)
{
if (s_Corners[i].x < corner1.x)
{
corner1.x = s_Corners[i].x;
}
else if (s_Corners[i].x > corner2.x)
{
corner2.x = s_Corners[i].x;
}
if (s_Corners[i].y < corner1.y)
{
corner1.y = s_Corners[i].y;
}
else if (s_Corners[i].y > corner2.y)
{
corner2.y = s_Corners[i].y;
}
}
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)
{
// Create renderer object.
var go = new GameObject("[generated] UIParticleRenderer", typeof(UIParticleRenderer))
var go = new GameObject("UIParticleRenderer", typeof(UIParticleRenderer))
{
hideFlags = HideFlags.DontSave,
layer = parent.gameObject.layer
layer = parent.gameObject.layer,
};
// Set parent.
@ -177,12 +112,7 @@ namespace Coffee.UIExtensions
{
_currentMaterialForRendering = null;
if (!IsActive() || !_parent)
{
ModifiedMaterial.Remove(_modifiedMaterial);
_modifiedMaterial = null;
return baseMaterial;
}
if (!IsActive()) return baseMaterial;
var modifiedMaterial = base.GetModifiedMaterial(baseMaterial);
@ -204,28 +134,50 @@ namespace Coffee.UIExtensions
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;
maskable = parent.maskable;
gameObject.layer = parent.gameObject.layer;
_particleSystem = ps;
_prewarm = _particleSystem.main.prewarm;
_particleSystem = particleSystem;
#if UNITY_EDITOR
if (Application.isPlaying)
#endif
{
if (_particleSystem.isPlaying || _prewarm)
if (_particleSystem.isPlaying)
{
_particleSystem.Clear();
_particleSystem.Pause();
}
}
_prewarm = _particleSystem.main.prewarm;
_renderer = ps.GetComponent<ParticleSystemRenderer>();
_renderer = particleSystem.GetComponent<ParticleSystemRenderer>();
_renderer.enabled = false;
//_emitter = emitter;
@ -236,11 +188,9 @@ namespace Coffee.UIExtensions
s_Materials.Clear();
// Support sprite.
var tsa = ps.textureSheetAnimation;
var tsa = particleSystem.textureSheetAnimation;
if (tsa.mode == ParticleSystemAnimationMode.Sprites && tsa.uvChannelMask == 0)
{
tsa.uvChannelMask = UVChannelFlags.UV0;
}
_prevScale = GetWorldScale();
_prevPsPos = _particleSystem.transform.position;
@ -257,15 +207,13 @@ namespace Coffee.UIExtensions
{
// No particle to render: Clear mesh.
if (
!isActiveAndEnabled || !_particleSystem || !_parent
|| !canvasRenderer || !canvas || !bakeCamera
|| _parent.meshSharing == UIParticle.MeshSharing.Replica
!isActiveAndEnabled || !_particleSystem || !_parent || !canvasRenderer || !canvas || !bakeCamera
|| _parent.meshSharing == UIParticle.MeshSharing.Reprica
|| !transform.lossyScale.GetScaled(_parent.scale3D).IsVisible() // Scale is not visible.
|| (!_particleSystem.IsAlive() && !_particleSystem.isPlaying) // No particle.
|| (_isTrail && !_particleSystem.trails.enabled) // Trail, but it is not enabled.
#if UNITY_2018_3_OR_NEWER
|| canvasRenderer.GetInheritedAlpha() <
0.01f // #102: Do not bake particle system to mesh when the alpha is zero.
|| canvasRenderer.GetInheritedAlpha() < 0.01f // #102: Do not bake particle system to mesh when the alpha is zero.
#endif
)
{
@ -303,25 +251,20 @@ namespace Coffee.UIExtensions
}
// When the ParticleSystem simulation is complete, stop it.
if (!main.loop
&& main.duration <= _particleSystem.time
&& (_particleSystem.IsAlive() || _particleSystem.particleCount == 0)
)
if (!main.loop && main.duration <= _particleSystem.time && (_particleSystem.IsAlive() || _particleSystem.particleCount == 0))
{
_particleSystem.Stop(false);
}
}
_prevScale = scale;
_prevPsPos = psPos;
_delay = false;
}
Profiler.EndSample();
// 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);
}
@ -333,41 +276,21 @@ namespace Coffee.UIExtensions
{
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();
// Combine mesh to transform. ([ParticleSystem local ->] world -> renderer local)
Profiler.BeginSample("[UIParticleRenderer] Combine Mesh");
if (_parent.canSimulate)
{
if (_parent.positionMode == UIParticle.PositionMode.Absolute)
if (_parent.absoluteMode)
{
s_CombineInstances[0].transform =
canvasRenderer.transform.worldToLocalMatrix
* GetWorldMatrix(psPos, scale);
s_CombineInstances[0].transform = canvasRenderer.transform.worldToLocalMatrix * GetWorldMatrix(psPos, scale);
}
else
{
var diff = _particleSystem.transform.position - _parent.transform.position;
s_CombineInstances[0].transform =
canvasRenderer.transform.worldToLocalMatrix
* Matrix4x4.Translate(diff.GetScaled(scale - Vector3.one))
* GetWorldMatrix(psPos, scale);
s_CombineInstances[0].transform = canvasRenderer.transform.worldToLocalMatrix * Matrix4x4.Translate(diff.GetScaled(scale - Vector3.one)) * GetWorldMatrix(psPos, scale);
}
workerMesh.CombineMeshes(s_CombineInstances, true, true);
workerMesh.RecalculateBounds();
@ -381,7 +304,6 @@ namespace Coffee.UIExtensions
workerMesh.bounds = bounds;
_lastBounds = bounds;
}
Profiler.EndSample();
@ -394,7 +316,7 @@ namespace Coffee.UIExtensions
// Set mesh to the CanvasRenderer.
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;
s_Renderers[i].canvasRenderer.SetMesh(workerMesh);
@ -405,7 +327,6 @@ namespace Coffee.UIExtensions
{
workerMesh.Clear();
}
canvasRenderer.SetMesh(workerMesh);
Profiler.EndSample();
@ -418,8 +339,7 @@ namespace Coffee.UIExtensions
{
_currentMaterialForRendering = materialForRendering;
}
for (var i = 0; i < s_Renderers.Count; i++)
for (int i = 0; i < s_Renderers.Count; i++)
{
if (s_Renderers[i] == this) continue;
@ -427,7 +347,6 @@ namespace Coffee.UIExtensions
s_Renderers[i].canvasRenderer.SetMaterial(_currentMaterialForRendering, 0);
}
}
Profiler.EndSample();
s_Renderers.Clear();
@ -439,6 +358,30 @@ namespace Coffee.UIExtensions
_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>
/// Call to update the geometry of the Graphic onto the CanvasRenderer.
/// </summary>
@ -448,9 +391,7 @@ namespace Coffee.UIExtensions
public override void Cull(Rect clipRect, bool validRect)
{
var cull = _lastBounds.extents == Vector3.zero
|| !validRect
|| !clipRect.Overlaps(rootCanvasRect, true);
var cull = _lastBounds.extents == Vector3.zero || !validRect || !clipRect.Overlaps(rootCanvasRect, true);
if (canvasRenderer.cull == cull) return;
canvasRenderer.cull = cull;
@ -462,7 +403,16 @@ namespace Coffee.UIExtensions
private Vector3 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();
return scale;
}
@ -502,7 +452,7 @@ namespace Coffee.UIExtensions
//* Matrix4x4.Translate(-wpos)
;
default:
throw new NotSupportedException();
throw new System.NotSupportedException();
}
}
@ -514,9 +464,8 @@ namespace Coffee.UIExtensions
private void ResolveResolutionChange(Vector3 psPos, Vector3 scale)
{
var screenSize = new Vector2Int(Screen.width, Screen.height);
var isWorldSpace = _particleSystem.IsWorldSpace();
var resolutionChanged = _prevScreenSize != screenSize || _prevScale != scale;
if (resolutionChanged && isWorldSpace)
//if ((_prevScreenSize != screenSize || _prevScale != scale) && _particleSystem.main.simulationSpace == ParticleSystemSimulationSpace.World && _parent.uiScaling)
if ((_prevScreenSize != screenSize || _prevScale != scale) && _particleSystem.main.simulationSpace == ParticleSystemSimulationSpace.World)
{
// Update particle array size and get particles.
var size = _particleSystem.particleCount;
@ -525,17 +474,13 @@ namespace Coffee.UIExtensions
// Resolusion resolver:
// (psPos / scale) / (prevPsPos / prevScale) -> psPos * scale.inv * prevPsPos.inv * prevScale
var modifier = psPos.GetScaled(
scale.Inverse(),
_prevPsPos.Inverse(),
_prevScale);
var modifier = psPos.GetScaled(scale.Inverse(), _prevPsPos.Inverse(), _prevScale);
for (var i = 0; i < size; i++)
{
var particle = particles[i];
particle.position = particle.position.GetScaled(modifier);
particles[i] = particle;
}
_particleSystem.SetParticles(particles, size);
// Delay: Do not progress in the frame where the resolution has been changed.
@ -543,7 +488,6 @@ namespace Coffee.UIExtensions
_prevScale = scale;
_prevPsPos = psPos;
}
_prevScreenSize = screenSize;
}
@ -563,8 +507,6 @@ namespace Coffee.UIExtensions
_prewarm = false;
}
// (COMMENT OUT) #231: Sub Emitters option is not work in editor playing
/*
// Emitted particles found.
if (_prevParticleCount != _particleSystem.particleCount)
{
@ -580,31 +522,24 @@ namespace Coffee.UIExtensions
_particleSystem.SetParticles(particles, size);
}
*/
// get world position.
var isLocalSpace = _particleSystem.IsLocalSpace();
var psTransform = _particleSystem.transform;
var originWorldPosition = psTransform.position;
var originWorldRotation = psTransform.rotation;
var emission = _particleSystem.emission;
var rateOverDistance = emission.enabled
&& 0 < emission.rateOverDistance.constant
&& 0 < emission.rateOverDistanceMultiplier;
if (rateOverDistance && !paused)
var rateOverDistance = emission.enabled && 0 < emission.rateOverDistance.constant && 0 < emission.rateOverDistanceMultiplier;
if (rateOverDistance)
{
// (For rate-over-distance emission,) Move to previous scaled position, simulate (delta = 0).
var prevScaledPos = isLocalSpace
? _prevPsPos
: _prevPsPos.GetScaled(_prevScale.Inverse());
Vector3 prevScaledPos = _prevPsPos.GetScaled(_prevScale.Inverse());
psTransform.SetPositionAndRotation(prevScaledPos, originWorldRotation);
_particleSystem.Simulate(0, false, false, false);
}
// Move to scaled position, simulate, revert to origin position.
var scaledPos = isLocalSpace
? originWorldPosition
: originWorldPosition.GetScaled(scale.Inverse());
var scaledPos = originWorldPosition.GetScaled(scale.Inverse());
psTransform.SetPositionAndRotation(scaledPos, originWorldRotation);
_particleSystem.Simulate(deltaTime, false, false, false);
psTransform.SetPositionAndRotation(originWorldPosition, originWorldRotation);
@ -614,8 +549,7 @@ namespace Coffee.UIExtensions
private void SimulateForEditor(Vector3 diffPos, Vector3 scale)
{
// Extra world simulation.
var isWorldSpace = _particleSystem.IsWorldSpace();
if (isWorldSpace && 0 < Vector3.SqrMagnitude(diffPos))
if (_particleSystem.main.simulationSpace == ParticleSystemSimulationSpace.World && 0 < Vector3.SqrMagnitude(diffPos))
{
Profiler.BeginSample("[UIParticle] Bake Mesh > Extra world simulation");
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 (s_Mpb == null)
{
s_Mpb = new MaterialPropertyBlock();
}
_renderer.GetPropertyBlock(s_Mpb);
if (s_Mpb.isEmpty) return;
@ -653,9 +585,8 @@ namespace Coffee.UIExtensions
// #41: Copy the value from MaterialPropertyBlock to CanvasRenderer
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);
}

View File

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

View File

@ -46,11 +46,11 @@ namespace Coffee.UIParticleExtensions
internal static class SpriteExtensions
{
#if UNITY_EDITOR
private static readonly Type s_SpriteEditorExtensionType =
private static Type tSpriteEditorExtension =
Type.GetType("UnityEditor.Experimental.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);
public static Texture2D GetActualTexture(this Sprite self)
@ -58,10 +58,8 @@ namespace Coffee.UIParticleExtensions
if (!self) return null;
if (Application.isPlaying) return self.texture;
var ret = s_GetActiveAtlasTextureMethodInfo.Invoke(null, new object[] { self }) as Texture2D;
return ret
? ret
: self.texture;
var ret = miGetActiveAtlasTexture.Invoke(null, new[] { self }) as Texture2D;
return ret ? ret : self.texture;
}
#else
internal static Texture2D GetActualTexture(this Sprite self)
@ -83,10 +81,8 @@ namespace Coffee.UIParticleExtensions
{
size = Mathf.NextPowerOfTwo(size);
}
s_TmpParticles = new ParticleSystem.Particle[size];
}
return s_TmpParticles;
}
@ -106,69 +102,47 @@ namespace Coffee.UIParticleExtensions
var main = self.main;
var space = main.simulationSpace;
if (space == ParticleSystemSimulationSpace.Custom && !main.customSimulationSpace)
{
space = ParticleSystemSimulationSpace.Local;
}
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)
{
self.Sort((a, b) =>
{
var tr = transform;
var aRenderer = a.GetComponent<ParticleSystemRenderer>();
var bRenderer = b.GetComponent<ParticleSystemRenderer>();
// Render queue: ascending
var aMat = aRenderer.sharedMaterial ? aRenderer.sharedMaterial : aRenderer.trailMaterial;
var bMat = bRenderer.sharedMaterial ? bRenderer.sharedMaterial : bRenderer.trailMaterial;
var aMat = aRenderer.sharedMaterial ?? aRenderer.trailMaterial;
var bMat = bRenderer.sharedMaterial ?? bRenderer.trailMaterial;
if (!aMat && !bMat) return 0;
if (!aMat) return -1;
if (!bMat) return 1;
if (sortByMaterial)
{
return aMat.GetInstanceID() - bMat.GetInstanceID();
}
if (aMat.renderQueue != bMat.renderQueue)
{
return aMat.renderQueue - bMat.renderQueue;
}
// Sorting layer: ascending
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
if (aRenderer.sortingOrder != bRenderer.sortingOrder)
{
return aRenderer.sortingOrder - bRenderer.sortingOrder;
}
// Z position & sortingFudge: descending
var aTransform = a.transform;
var bTransform = b.transform;
var aPos = transform.InverseTransformPoint(aTransform.position).z + aRenderer.sortingFudge;
var bPos = transform.InverseTransformPoint(bTransform.position).z + bRenderer.sortingFudge;
var aPos = tr.InverseTransformPoint(aTransform.position).z + aRenderer.sortingFudge;
var bPos = tr.InverseTransformPoint(bTransform.position).z + bRenderer.sortingFudge;
if (!Mathf.Approximately(aPos, bPos))
{
return (int)Mathf.Sign(bPos - aPos);
}
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++)
{
if (list[i].GetInstanceID() == ps.GetInstanceID())
{
return i;
}
if (list[i].GetInstanceID() == ps.GetInstanceID()) return i;
}
return 0;
@ -212,55 +183,4 @@ namespace Coffee.UIParticleExtensions
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",
"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.",
"version": "4.2.1",
"version": "4.1.7",
"unity": "2018.2",
"license": "MIT",
"repository": {