添加 Essential Kit - Mobile (iOS & Android) 3.2.1 插件 文档地址:https://assetstore.essentialkit.voxelbusters.com/whats-new-in-v3/version-3-vs-version-2
This commit is contained in:
parent
4c6ce570c1
commit
3448c86bc2
8
EintooAR/Assets/Editor/ExternalDependencyManager.meta
Normal file
8
EintooAR/Assets/Editor/ExternalDependencyManager.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e08fb507e0b034036b8cc7edd9deeb68
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 60eed25df4a46427aa405831f8a1c54a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5467eebe589ed445d8de6152fa6beb06
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
@ -0,0 +1,38 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a366c71c93ae411ea1ece423e8b1538f
|
||||||
|
labels:
|
||||||
|
- gvh
|
||||||
|
- gvh_version-1.2.183
|
||||||
|
- gvhp_exportpath-ExternalDependencyManager/Editor/1.2.183/Google.IOSResolver.dll
|
||||||
|
- gvhp_targets-editor
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
- first:
|
||||||
|
Windows Store Apps: WindowsStoreApps
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
@ -0,0 +1,38 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9cab53159f0a43b68c16552c892bd579
|
||||||
|
labels:
|
||||||
|
- gvh
|
||||||
|
- gvh_version-1.2.183
|
||||||
|
- gvhp_exportpath-ExternalDependencyManager/Editor/1.2.183/Google.JarResolver.dll
|
||||||
|
- gvhp_targets-editor
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
- first:
|
||||||
|
Windows Store Apps: WindowsStoreApps
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
@ -0,0 +1,38 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 06f4aa8c065049e5970eedec5f52600f
|
||||||
|
labels:
|
||||||
|
- gvh
|
||||||
|
- gvh_version-1.2.183
|
||||||
|
- gvhp_exportpath-ExternalDependencyManager/Editor/1.2.183/Google.PackageManagerResolver.dll
|
||||||
|
- gvhp_targets-editor
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
- first:
|
||||||
|
Windows Store Apps: WindowsStoreApps
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
@ -0,0 +1,38 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ad4fd0d2f20345839aa31291c8c0c285
|
||||||
|
labels:
|
||||||
|
- gvh
|
||||||
|
- gvh_version-1.2.183
|
||||||
|
- gvhp_exportpath-ExternalDependencyManager/Editor/1.2.183/Google.VersionHandlerImpl.dll
|
||||||
|
- gvhp_targets-editor
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
- first:
|
||||||
|
Windows Store Apps: WindowsStoreApps
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
1417
EintooAR/Assets/Editor/ExternalDependencyManager/Editor/CHANGELOG.md
Normal file
1417
EintooAR/Assets/Editor/ExternalDependencyManager/Editor/CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2b4957d4e158489ca7b410312d84e001
|
||||||
|
labels:
|
||||||
|
- gvh
|
||||||
|
- gvh_version-1.2.183
|
||||||
|
- gvhp_exportpath-ExternalDependencyManager/Editor/CHANGELOG.md
|
||||||
|
timeCreated: 1584567712
|
||||||
|
licenseType: Store
|
||||||
|
TextScriptImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
@ -0,0 +1,36 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 164f2f671ce9410dadebde5826bc0b51
|
||||||
|
labels:
|
||||||
|
- gvh
|
||||||
|
- gvh_version-1.2.183
|
||||||
|
- gvhp_exportpath-ExternalDependencyManager/Editor/Google.VersionHandler.dll
|
||||||
|
- gvhp_targets-editor
|
||||||
|
timeCreated: 1480838400
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
- first:
|
||||||
|
Windows Store Apps: WindowsStoreApps
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
245
EintooAR/Assets/Editor/ExternalDependencyManager/Editor/LICENSE
Normal file
245
EintooAR/Assets/Editor/ExternalDependencyManager/Editor/LICENSE
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
Copyright (C) 2014 Google Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
====================================================================================================
|
||||||
|
This package uses MiniJSON
|
||||||
|
|
||||||
|
Copyright (c) 2013 Calvin Rien
|
||||||
|
|
||||||
|
Based on the JSON parser by Patrick van Bergen
|
||||||
|
http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
|
||||||
|
|
||||||
|
Simplified it so that it doesn't throw exceptions
|
||||||
|
and can be used in Unity iPhone with maximum code stripping.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 805e2c2846dd44248246ef896b03c176
|
||||||
|
labels:
|
||||||
|
- gvh
|
||||||
|
- gvh_version-1.2.183
|
||||||
|
- gvhp_exportpath-ExternalDependencyManager/Editor/LICENSE
|
||||||
|
timeCreated: 1584567712
|
||||||
|
licenseType: Store
|
||||||
|
TextScriptImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,897 @@
|
|||||||
|
# External Dependency Manager for Unity
|
||||||
|
|
||||||
|
[](https://openupm.com/packages/com.google.external-dependency-manager/)
|
||||||
|
[](https://openupm.com/packages/com.google.external-dependency-manager/)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The External Dependency Manager for Unity (EDM4U) (formerly Play Services
|
||||||
|
Resolver/Jar Resolver) is intended to be used by any Unity package or user that
|
||||||
|
requires:
|
||||||
|
|
||||||
|
* Android specific libraries (e.g
|
||||||
|
[AARs](https://developer.android.com/studio/projects/android-library.html))
|
||||||
|
|
||||||
|
* iOS [CocoaPods](https://cocoapods.org/)
|
||||||
|
|
||||||
|
* Version management of transitive dependencies
|
||||||
|
|
||||||
|
* Management of Package Manager (PM) Registries
|
||||||
|
|
||||||
|
If you want to add and use iOS/Android dependencies directly in your project,
|
||||||
|
then you should to install EDM4U in your project.
|
||||||
|
|
||||||
|
If you are a package user and the plugin you are using depends on EDM4U, *and*
|
||||||
|
the package does not include EDM4U as a package dependency already, then you
|
||||||
|
should to install EDM4U in your project.
|
||||||
|
|
||||||
|
If you are a UPM package maintainer and your package requires EDM4U, then you
|
||||||
|
should add EDM4U as a
|
||||||
|
[package dependency](https://docs.unity3d.com/2019.3/Documentation/Manual/upm-dependencies.html)
|
||||||
|
in your package manifest (`package.json`):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"com.google.external-dependency-manager": "1.2.178"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You should still install EDM4U to test out the package during development.
|
||||||
|
|
||||||
|
If you are a legacy `.unitypackage` package maintainer and your package requires
|
||||||
|
EDM4U, please ask the user to install EDM4U separately. You should install EDM4U
|
||||||
|
to test out the package during development.
|
||||||
|
|
||||||
|
Updated releases are available on
|
||||||
|
[GitHub](https://github.com/googlesamples/unity-jar-resolver)
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
The *Android Resolver* and *iOS Resolver* components of the plugin only work
|
||||||
|
with Unity version 4.6.8 or higher.
|
||||||
|
|
||||||
|
The *Version Handler* component only works with Unity 5.x or higher as it
|
||||||
|
depends upon the `PluginImporter` UnityEditor API.
|
||||||
|
|
||||||
|
The *Package Manager Resolver* component only works with Unity 2018.4 or above,
|
||||||
|
when [scoped registry](https://docs.unity3d.com/Manual/upm-scoped.html) support
|
||||||
|
was added to the Package Manager.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
Check out [troubleshooting](troubleshooting-faq.md) if you need help.
|
||||||
|
|
||||||
|
### Install via OpenUPM
|
||||||
|
|
||||||
|
EDM4U is available on
|
||||||
|
[OpenUPM](https://openupm.com/packages/com.google.external-dependency-manager/):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
openupm add com.google.external-dependency-manager
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install via Google APIs for Unity
|
||||||
|
|
||||||
|
EDM4U is available both in UPM and legacy `.unitypackage` formats on
|
||||||
|
[Google APIs for Unity](https://developers.google.com/unity/archive#external_dependency_manager_for_unity).
|
||||||
|
|
||||||
|
You may install the UPM version (.tgz) as a
|
||||||
|
[local UPM package](https://docs.unity3d.com/Manual/upm-ui-local.html).
|
||||||
|
|
||||||
|
You can also install EDM4U in your project as a `.unitypackage`. This is not
|
||||||
|
recommended due to potential conflicts.
|
||||||
|
|
||||||
|
### Conflict Resolution
|
||||||
|
|
||||||
|
For historical reasons, a package maintainer may choose to embed EDM4U in their
|
||||||
|
package for ease of installation. This will create a conflict when you try to
|
||||||
|
install EDM4U with the steps above, or with another package with embedded EDM4U.
|
||||||
|
If your project imported a `.unitypackage` that has a copy of EDM4U embedded in
|
||||||
|
it, you may safely delete it from your Assets folder. If your project depends on
|
||||||
|
another UPM package with EDM4U, please reach out to the package maintainer and
|
||||||
|
ask them to replace it with a dependency to this package. In the meantime, you
|
||||||
|
can workaround the issue by copying the package to your Packages folder (to
|
||||||
|
create an
|
||||||
|
[embedded package](https://docs.unity3d.com/Manual/upm-concepts.html#Embedded))
|
||||||
|
and perform the steps yourself to avoid a dependency conflict.
|
||||||
|
|
||||||
|
### Config file
|
||||||
|
|
||||||
|
To start adding dependencies to your project, copy and rename the
|
||||||
|
[SampleDependencies.xml](https://github.com/googlesamples/unity-jar-resolver/blob/master/sample/Assets/ExternalDependencyManager/Editor/SampleDependencies.xml)
|
||||||
|
file into your plugin and add the dependencies your project requires.
|
||||||
|
|
||||||
|
The XML file needs to be under an `Editor` directory and match the name
|
||||||
|
`*Dependencies.xml`. For example, `MyPlugin/Editor/MyPluginDependencies.xml`.
|
||||||
|
|
||||||
|
## Usages
|
||||||
|
|
||||||
|
### Android Resolver
|
||||||
|
|
||||||
|
The Android Resolver copies specified dependencies from local or remote Maven
|
||||||
|
repositories into the Unity project when a user selects Android as the build
|
||||||
|
target in the Unity editor.
|
||||||
|
|
||||||
|
For example, to add the Google Play Games library
|
||||||
|
(`com.google.android.gms:play-services-games` package) at version `9.8.0` to the
|
||||||
|
set of a plugin's Android dependencies:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependencies>
|
||||||
|
<androidPackages>
|
||||||
|
<androidPackage spec="com.google.android.gms:play-services-games:9.8.0">
|
||||||
|
<androidSdkPackageIds>
|
||||||
|
<androidSdkPackageId>extra-google-m2repository</androidSdkPackageId>
|
||||||
|
</androidSdkPackageIds>
|
||||||
|
</androidPackage>
|
||||||
|
</androidPackages>
|
||||||
|
</dependencies>
|
||||||
|
```
|
||||||
|
|
||||||
|
The version specification (last component) supports:
|
||||||
|
|
||||||
|
* Specific versions e.g `9.8.0`
|
||||||
|
|
||||||
|
* Partial matches e.g `9.8.+` would match 9.8.0, 9.8.1 etc. choosing the most
|
||||||
|
recent version
|
||||||
|
|
||||||
|
* Latest version using `LATEST` or `+`. We do *not* recommend using this
|
||||||
|
unless you're 100% sure the library you depend upon will not break your
|
||||||
|
Unity plugin in future
|
||||||
|
|
||||||
|
The above example specifies the dependency as a component of the Android SDK
|
||||||
|
manager such that the Android SDK manager will be executed to install the
|
||||||
|
package if it's not found. If your Android dependency is located on Maven
|
||||||
|
central it's possible to specify the package simply using the `androidPackage`
|
||||||
|
element:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependencies>
|
||||||
|
<androidPackages>
|
||||||
|
<androidPackage spec="com.google.api-client:google-api-client-android:1.22.0" />
|
||||||
|
</androidPackages>
|
||||||
|
</dependencies>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Auto-resolution
|
||||||
|
|
||||||
|
By default the Android Resolver automatically monitors the dependencies you have
|
||||||
|
specified and the `Plugins/Android` folder of your Unity project. The resolution
|
||||||
|
process runs when the specified dependencies are not present in your project.
|
||||||
|
|
||||||
|
The *auto-resolution* process can be disabled via the `Assets > External
|
||||||
|
Dependency Manager > Android Resolver > Settings` menu.
|
||||||
|
|
||||||
|
Manual resolution can be performed using the following menu options:
|
||||||
|
|
||||||
|
* `Assets > External Dependency Manager > Android Resolver > Resolve`
|
||||||
|
|
||||||
|
* `Assets > External Dependency Manager > Android Resolver > Force Resolve`
|
||||||
|
|
||||||
|
#### Deleting libraries
|
||||||
|
|
||||||
|
Resolved packages are tracked via asset labels by the Android Resolver. They can
|
||||||
|
easily be deleted using the `Assets > External Dependency Manager > Android
|
||||||
|
Resolver > Delete Resolved Libraries` menu item.
|
||||||
|
|
||||||
|
#### Android Manifest Variable Processing
|
||||||
|
|
||||||
|
Some AAR files (for example play-services-measurement) contain variables that
|
||||||
|
are processed by the Android Gradle plugin. Unfortunately, Unity does not
|
||||||
|
perform the same processing when using Unity's Internal Build System, so the
|
||||||
|
Android Resolver plugin handles known cases of this variable substitution by
|
||||||
|
exploding the AAR into a folder and replacing `${applicationId}` with the
|
||||||
|
`bundleID`.
|
||||||
|
|
||||||
|
Disabling AAR explosion and therefore Android manifest processing can be done
|
||||||
|
via the `Assets > External Dependency Manager > Android Resolver > Settings`
|
||||||
|
menu. You may want to disable explosion of AARs if you're exporting a project to
|
||||||
|
be built with Gradle/Android Studio.
|
||||||
|
|
||||||
|
#### ABI Stripping
|
||||||
|
|
||||||
|
Some AAR files contain native libraries (.so files) for each ABI supported by
|
||||||
|
Android. Unfortunately, when targeting a single ABI (e.g x86), Unity does not
|
||||||
|
strip native libraries for unused ABIs. To strip unused ABIs, the Android
|
||||||
|
Resolver plugin explodes an AAR into a folder and removes unused ABIs to reduce
|
||||||
|
the built APK size. Furthermore, if native libraries are not stripped from an
|
||||||
|
APK (e.g you have a mix of Unity's x86 library and some armeabi-v7a libraries)
|
||||||
|
Android may attempt to load the wrong library for the current runtime ABI
|
||||||
|
completely breaking your plugin when targeting some architectures.
|
||||||
|
|
||||||
|
AAR explosion and therefore ABI stripping can be disabled via the `Assets >
|
||||||
|
External Dependency Manager > Android Resolver > Settings` menu. You may want to
|
||||||
|
disable explosion of AARs if you're exporting a project to be built with
|
||||||
|
Gradle/Android Studio.
|
||||||
|
|
||||||
|
#### Resolution Strategies
|
||||||
|
|
||||||
|
By default the Android Resolver will use Gradle to download dependencies prior
|
||||||
|
to integrating them into a Unity project. This works with Unity's internal build
|
||||||
|
system and Gradle/Android Studio project export.
|
||||||
|
|
||||||
|
It's possible to change the resolution strategy via the `Assets > External
|
||||||
|
Dependency Manager > Android Resolver > Settings` menu.
|
||||||
|
|
||||||
|
##### Download Artifacts with Gradle
|
||||||
|
|
||||||
|
Using the default resolution strategy, the Android resolver executes the
|
||||||
|
following operations:
|
||||||
|
|
||||||
|
- Remove the result of previous Android resolutions. E.g Delete all files and
|
||||||
|
directories labeled with "gpsr" under `Plugins/Android` from the project.
|
||||||
|
|
||||||
|
- Collect the set of Android dependencies (libraries) specified by a project's
|
||||||
|
`*Dependencies.xml` files.
|
||||||
|
|
||||||
|
- Run `download_artifacts.gradle` with Gradle to resolve conflicts and, if
|
||||||
|
successful, download the set of resolved Android libraries (AARs, JARs).
|
||||||
|
|
||||||
|
- Process each AAR/JAR so that it can be used with the currently selected
|
||||||
|
Unity build system (e.g Internal vs. Gradle, Export vs. No Export). This
|
||||||
|
involves patching each reference to `applicationId` in the
|
||||||
|
`AndroidManifest.xml` with the project's bundle ID. This means resolution
|
||||||
|
must be run again if the bundle ID has changed.
|
||||||
|
|
||||||
|
- Move the processed AARs to `Plugins/Android` so they will be included when
|
||||||
|
Unity invokes the Android build.
|
||||||
|
|
||||||
|
##### Integrate into mainTemplate.gradle
|
||||||
|
|
||||||
|
Unity 5.6 introduced support for customizing the `build.gradle` used to build
|
||||||
|
Unity projects with Gradle. When the *Patch mainTemplate.gradle* setting is
|
||||||
|
enabled, rather than downloading artifacts before the build, Android resolution
|
||||||
|
results in the execution of the following operations:
|
||||||
|
|
||||||
|
- Remove the result of previous Android resolutions. E.g Delete all files and
|
||||||
|
directories labeled with "gpsr" under `Plugins/Android` from the project and
|
||||||
|
remove sections delimited with `// Android Resolver * Start` and `// Android
|
||||||
|
Resolver * End` lines.
|
||||||
|
|
||||||
|
- Collect the set of Android dependencies (libraries) specified by a project's
|
||||||
|
`*Dependencies.xml` files.
|
||||||
|
|
||||||
|
- Rename any `.srcaar` files in the build to `.aar` and exclude them from
|
||||||
|
being included directly by Unity in the Android build as
|
||||||
|
`mainTemplate.gradle` will be patched to include them instead from their
|
||||||
|
local maven repositories.
|
||||||
|
|
||||||
|
- Inject the required Gradle repositories into `mainTemplate.gradle` at the
|
||||||
|
line matching the pattern `.*apply plugin:
|
||||||
|
'com\.android\.(application|library)'.*` or the section starting at the line
|
||||||
|
`// Android Resolver Repos Start`. If you want to control the injection
|
||||||
|
point in the file, the section delimited by the lines `// Android Resolver
|
||||||
|
Repos Start` and `// Android Resolver Repos End` should be placed in the
|
||||||
|
global scope before the `dependencies` section.
|
||||||
|
|
||||||
|
- Inject the required Android dependencies (libraries) into
|
||||||
|
`mainTemplate.gradle` at the line matching the pattern `***DEPS***` or the
|
||||||
|
section starting at the line `// Android Resolver Dependencies Start`. If
|
||||||
|
you want to control the injection point in the file, the section delimited
|
||||||
|
by the lines `// Android Resolver Dependencies Start` and `// Android
|
||||||
|
Resolver Dependencies End` should be placed in the `dependencies` section.
|
||||||
|
|
||||||
|
- Inject the packaging options logic, which excludes architecture specific
|
||||||
|
libraries based upon the selected build target, into `mainTemplate.gradle`
|
||||||
|
at the line matching the pattern `android +{` or the section starting at the
|
||||||
|
line `// Android Resolver Exclusions Start`. If you want to control the
|
||||||
|
injection point in the file, the section delimited by the lines `// Android
|
||||||
|
Resolver Exclusions Start` and `// Android Resolver Exclusions End` should
|
||||||
|
be placed in the global scope before the `android` section.
|
||||||
|
|
||||||
|
#### Dependency Tracking
|
||||||
|
|
||||||
|
The Android Resolver creates the
|
||||||
|
`ProjectSettings/AndroidResolverDependencies.xml` to quickly determine the set
|
||||||
|
of resolved dependencies in a project. This is used by the auto-resolution
|
||||||
|
process to only run the expensive resolution process when necessary.
|
||||||
|
|
||||||
|
#### Displaying Dependencies
|
||||||
|
|
||||||
|
It's possible to display the set of dependencies the Android Resolver would
|
||||||
|
download and process in your project via the `Assets > External Dependency
|
||||||
|
Manager > Android Resolver > Display Libraries` menu item.
|
||||||
|
|
||||||
|
### iOS Resolver
|
||||||
|
|
||||||
|
The iOS resolver component of this plugin manages
|
||||||
|
[CocoaPods](https://cocoapods.org/). A CocoaPods `Podfile` is generated and the
|
||||||
|
`pod` tool is executed as a post build process step to add dependencies to the
|
||||||
|
Xcode project exported by Unity.
|
||||||
|
|
||||||
|
Dependencies for iOS are added by referring to CocoaPods.
|
||||||
|
|
||||||
|
For example, to add the AdMob pod, version 7.0 or greater with bitcode enabled:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependencies>
|
||||||
|
<iosPods>
|
||||||
|
<iosPod name="Google-Mobile-Ads-SDK" version="~> 7.0" bitcodeEnabled="true"
|
||||||
|
minTargetSdk="6.0" addToAllTargets="false" />
|
||||||
|
</iosPods>
|
||||||
|
</dependencies>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Integration Strategies
|
||||||
|
|
||||||
|
The `CocoaPods` are either:
|
||||||
|
|
||||||
|
* Downloaded and injected into the Xcode project file directly, rather than
|
||||||
|
creating a separate xcworkspace. We call this `Xcode project` integration.
|
||||||
|
|
||||||
|
* If the Unity version supports opening a xcworkspace file, the `pod` tool is
|
||||||
|
used as intended to generate a xcworkspace which references the CocoaPods.
|
||||||
|
We call this `Xcode workspace` integration.
|
||||||
|
|
||||||
|
The resolution strategy can be changed via the `Assets > External Dependency
|
||||||
|
Manager > iOS Resolver > Settings` menu.
|
||||||
|
|
||||||
|
##### Appending text to generated Podfile
|
||||||
|
|
||||||
|
In order to modify the generated Podfile you can create a script like this:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.Callbacks;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class PostProcessIOS : MonoBehaviour
|
||||||
|
{
|
||||||
|
// Must be between 40 and 50 to ensure that it's not overriden by Podfile generation (40) and
|
||||||
|
// that it's added before "pod install" (50).
|
||||||
|
[PostProcessBuildAttribute(45)]
|
||||||
|
private static void PostProcessBuild_iOS(BuildTarget target, string buildPath)
|
||||||
|
{
|
||||||
|
if (target == BuildTarget.iOS)
|
||||||
|
{
|
||||||
|
using (StreamWriter sw = File.AppendText(buildPath + "/Podfile"))
|
||||||
|
{
|
||||||
|
// E.g. add an app extension
|
||||||
|
sw.WriteLine("\ntarget 'NSExtension' do\n pod 'Firebase/Messaging', '6.6.0'\nend");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Package Manager Resolver
|
||||||
|
|
||||||
|
Adding registries to the
|
||||||
|
[Package Manager](https://docs.unity3d.com/Manual/Packages.html) (PM) is a
|
||||||
|
manual process. The Package Manager Resolver (PMR) component of this plugin
|
||||||
|
makes it easy for plugin maintainers to distribute new PM registry servers and
|
||||||
|
easy for plugin users to manage PM registry servers.
|
||||||
|
|
||||||
|
#### Adding Registries
|
||||||
|
|
||||||
|
For example, to add a registry for plugins in the scope `com.coolstuff`:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<registries>
|
||||||
|
<registry name="Cool Stuff"
|
||||||
|
url="https://unityregistry.coolstuff.com"
|
||||||
|
termsOfService="https://coolstuff.com/unityregistry/terms"
|
||||||
|
privacyPolicy="https://coolstuff.com/unityregistry/privacy">
|
||||||
|
<scopes>
|
||||||
|
<scope>com.coolstuff</scope>
|
||||||
|
</scopes>
|
||||||
|
</registry>
|
||||||
|
</registries>
|
||||||
|
```
|
||||||
|
|
||||||
|
When PMR is loaded it will prompt the developer to add the registry to their
|
||||||
|
project if it isn't already present in the `Packages/manifest.json` file.
|
||||||
|
|
||||||
|
For more information, see Unity's documentation on
|
||||||
|
[scoped package registries](https://docs.unity3d.com/Manual/upm-scoped.html).
|
||||||
|
|
||||||
|
#### Managing Registries
|
||||||
|
|
||||||
|
It's possible to add and remove registries that are specified via PMR XML
|
||||||
|
configuration files via the following menu options:
|
||||||
|
|
||||||
|
* `Assets > External Dependency Manager > Package Manager Resolver > Add
|
||||||
|
Registries` will prompt the user with a window which allows them to add
|
||||||
|
registries discovered in the project to the Package Manager.
|
||||||
|
|
||||||
|
* `Assets > External Dependency Manager > Package Manager Resolver > Remove
|
||||||
|
Registries` will prompt the user with a window which allows them to remove
|
||||||
|
registries discovered in the project from the Package Manager.
|
||||||
|
|
||||||
|
* `Assets > External Dependency Manager > Package Manager Resolver > Modify
|
||||||
|
Registries` will prompt the user with a window which allows them to add or
|
||||||
|
remove registries discovered in the project.
|
||||||
|
|
||||||
|
#### Migration
|
||||||
|
|
||||||
|
PMR can migrate Version Handler packages installed in the `Assets` folder to PM
|
||||||
|
packages. This requires the plugins to implement the following:
|
||||||
|
|
||||||
|
* `.unitypackage` must include a Version Handler manifests that describes the
|
||||||
|
components of the plugin. If the plugin has no dependencies the manifest
|
||||||
|
would just include the files in the plugin.
|
||||||
|
|
||||||
|
* The PM package JSON provided by the registry must include a keyword (in the
|
||||||
|
`versions.VERSION.keyword` list) that maps the PM package to a Version
|
||||||
|
Handler package using the format `vh-name:VERSION_HANDLER_MANIFEST_NAME`
|
||||||
|
where `VERSION_HANDLER_MANIFEST_NAME` is the name of the manifest defined in
|
||||||
|
the `.unitypackage`. For more information see the description of the
|
||||||
|
`gvhp_manifestname` asset label in the [Version Handler](#version-handler)
|
||||||
|
section.
|
||||||
|
|
||||||
|
When using the `Assets > External Dependency Manager > Package Manager
|
||||||
|
Resolver > Migrate Packages` menu option, PMR then will:
|
||||||
|
|
||||||
|
* List all Version Handler manager packages in the project.
|
||||||
|
|
||||||
|
* Search all available packages in the PM registries and fetch keywords
|
||||||
|
associated with each package parsing the Version Handler manifest names for
|
||||||
|
each package.
|
||||||
|
|
||||||
|
* Map each installed Version Handler package to a PM package.
|
||||||
|
|
||||||
|
* Prompt the user to migrate the discovered packages.
|
||||||
|
|
||||||
|
* Perform package migration for all selected packages if the user clicks the
|
||||||
|
`Apply` button.
|
||||||
|
|
||||||
|
#### Configuration
|
||||||
|
|
||||||
|
PMR can be configured via the `Assets > External Dependency Manager > Package
|
||||||
|
Manager Resolver > Settings` menu option:
|
||||||
|
|
||||||
|
* `Add package registries` when enabled, when the plugin loads or registry
|
||||||
|
configuration files change, this will prompt the user to add registries that
|
||||||
|
are not present in the Package Manager.
|
||||||
|
|
||||||
|
* `Prompt to add package registries` will cause a developer to be prompted
|
||||||
|
with a window that will ask for confirmation before adding registries. When
|
||||||
|
this is disabled registries are added silently to the project.
|
||||||
|
|
||||||
|
* `Prompt to migrate packages` will cause a developer to be prompted with a
|
||||||
|
window that will ask for confirmation before migrating packages installed in
|
||||||
|
the `Assets` directory to PM packages.
|
||||||
|
|
||||||
|
* `Enable Analytics Reporting` when enabled, reports the use of the plugin to
|
||||||
|
the developers so they can make imrpovements.
|
||||||
|
|
||||||
|
* `Verbose logging` when enabled prints debug information to the console which
|
||||||
|
can be useful when filing bug reports.
|
||||||
|
|
||||||
|
### Version Handler
|
||||||
|
|
||||||
|
The Version Handler component of this plugin manages:
|
||||||
|
|
||||||
|
* Shared Unity plugin dependencies.
|
||||||
|
|
||||||
|
* Upgrading Unity plugins by cleaning up old files from previous versions.
|
||||||
|
|
||||||
|
* Uninstallation of plugins that are distributed with manifest files.
|
||||||
|
|
||||||
|
* Restoration of plugin assets to their original install locations if assets
|
||||||
|
are tagged with the `exportpath` label.
|
||||||
|
|
||||||
|
Since the Version Handler needs to modify Unity asset metadata (`.meta` files),
|
||||||
|
to enable/disable components, rename and delete asset files it does not work
|
||||||
|
with Package Manager installed packages. It's still possible to include EDM4U in
|
||||||
|
Package Manager packages, the Version Handler component simply won't do anything
|
||||||
|
to PM plugins in this case.
|
||||||
|
|
||||||
|
#### Using Version Handler Managed Plugins
|
||||||
|
|
||||||
|
If a plugin is imported at multiple different versions into a project, if the
|
||||||
|
Version Handler is enabled, it will automatically check all managed assets to
|
||||||
|
determine the set of assets that are out of date and assets that should be
|
||||||
|
removed. To disable automatic checking managed assets disable the `Enable
|
||||||
|
version management` option in the `Assets > External Dependency Manager >
|
||||||
|
Version Handler > Settings` menu.
|
||||||
|
|
||||||
|
If version management is disabled, it's possible to check managed assets
|
||||||
|
manually using the `Assets > External Dependency Manager > Version Handler >
|
||||||
|
Update` menu option.
|
||||||
|
|
||||||
|
##### Listing Managed Plugins
|
||||||
|
|
||||||
|
Plugins managed by the Version Handler, those that ship with manifest files, can
|
||||||
|
displayed using the `Assets > External Dependency Manager > Version Handler >
|
||||||
|
Display Managed Packages` menu option. The list of plugins are written to the
|
||||||
|
console window along with the set of files used by each plugin.
|
||||||
|
|
||||||
|
##### Uninstalling Managed Plugins
|
||||||
|
|
||||||
|
Plugins managed by the Version Handler, those that ship with manifest files, can
|
||||||
|
be removed using the `Assets > External Dependency Manager > Version Handler >
|
||||||
|
Uninstall Managed Packages` menu option. This operation will display a window
|
||||||
|
that allows a developer to select a set of plugins to remove which will remove
|
||||||
|
all files owned by each plugin excluding those that are in use by other
|
||||||
|
installed plugins.
|
||||||
|
|
||||||
|
Files managed by the Version Handler, those labeled with the `gvh` asset label,
|
||||||
|
can be checked to see whether anything needs to be upgraded, disabled or removed
|
||||||
|
using the `Assets > External Dependency Manager > Version Handler > Update` menu
|
||||||
|
option.
|
||||||
|
|
||||||
|
##### Restore Install Paths
|
||||||
|
|
||||||
|
Some developers move assets around in their project which can make it harder for
|
||||||
|
plugin maintainers to debug issues if this breaks Unity's
|
||||||
|
[special folders](https://docs.unity3d.com/Manual/SpecialFolders.html) rules. If
|
||||||
|
assets are labeled with their original install/export path (see
|
||||||
|
`gvhp_exportpath` below), Version Handler can restore assets to their original
|
||||||
|
locations when using the `Assets > External Dependency Manager > Version
|
||||||
|
Handler > Move Files To Install Locations` menu option.
|
||||||
|
|
||||||
|
##### Settings
|
||||||
|
|
||||||
|
Some behavior of the Version Handler can be configured via the `Assets >
|
||||||
|
External Dependency Manager > Version Handler > Settings` menu option.
|
||||||
|
|
||||||
|
* `Enable version management` controls whether the plugin should automatically
|
||||||
|
check asset versions and apply changes. If this is disabled the process
|
||||||
|
should be run manually when installing or upgrading managed plugins using
|
||||||
|
`Assets > External Dependency Manager > Version Handler > Update`.
|
||||||
|
|
||||||
|
* `Rename to canonical filenames` is a legacy option that will rename files to
|
||||||
|
remove version numbers and other labels from filenames.
|
||||||
|
|
||||||
|
* `Prompt for obsolete file deletion` enables the display of a window when
|
||||||
|
obsolete files are deleted allowing the developer to select which files to
|
||||||
|
delete and those to keep.
|
||||||
|
|
||||||
|
* `Allow disabling files via renaming` controls whether obsolete or disabled
|
||||||
|
files should be disabled by renaming them to `myfilename_DISABLED`. Renaming
|
||||||
|
to disable files is required in some scenarios where Unity doesn't support
|
||||||
|
removing files from the build via the PluginImporter.
|
||||||
|
|
||||||
|
* `Enable Analytics Reporting` enables/disables usage reporting to plugin
|
||||||
|
developers to improve the product.
|
||||||
|
|
||||||
|
* `Verbose logging` enables *very* noisy log output that is useful for
|
||||||
|
debugging while filing a bug report or building a new managed plugin.
|
||||||
|
|
||||||
|
* `Use project settings` saves settings for the plugin in the project rather
|
||||||
|
than system-wide.
|
||||||
|
|
||||||
|
#### Redistributing a Managed Plugin
|
||||||
|
|
||||||
|
The Version Handler employs a couple of methods for managing version selection,
|
||||||
|
upgrade and removal of plugins.
|
||||||
|
|
||||||
|
* Each plugin can ship with a manifest file that lists the files it includes.
|
||||||
|
This makes it possible for Version Handler to calculate the difference in
|
||||||
|
assets between the most recent release of a plugin and the previous release
|
||||||
|
installed in a project. If a files are removed the Version Handler will
|
||||||
|
prompt the user to clean up obsolete files.
|
||||||
|
|
||||||
|
* Plugins can ship using assets with unique names, unique GUIDs and version
|
||||||
|
number labels. Version numbers can be attached to assets using labels or
|
||||||
|
added to the filename (e.g `myfile.txt` would be `myfile_version-x.y.z.txt).
|
||||||
|
This allows the Version Handler to determine which set of files are the same
|
||||||
|
file at different versions, select the most recent version and prompt the
|
||||||
|
developer to clean up old versions.
|
||||||
|
|
||||||
|
Unity plugins can be managed by the Version Handler using the following steps:
|
||||||
|
|
||||||
|
1. Add the `gvh` asset label to each asset (file) you want Version Handler to
|
||||||
|
manage.
|
||||||
|
|
||||||
|
1. Add the `gvh_version-VERSION` label to each asset where `VERSION` is the
|
||||||
|
version of the plugin you're releasing (e.g 1.2.3).
|
||||||
|
|
||||||
|
1. Add the `gvhp_exportpath-PATH` label to each asset where `PATH` is the
|
||||||
|
export path of the file when the `.unitypackage` is created. This is used to
|
||||||
|
track files if they're moved around in a project by developers.
|
||||||
|
|
||||||
|
1. Optional: Add `gvh_targets-editor` label to each editor DLL in your plugin
|
||||||
|
and disable `editor` as a target platform for the DLL. The Version Handler
|
||||||
|
will enable the most recent version of this DLL when the plugin is imported.
|
||||||
|
|
||||||
|
1. Optional: If your plugin is included in other Unity plugins, you should add
|
||||||
|
the version number to each filename and change the GUID of each asset. This
|
||||||
|
allows multiple versions of your plugin to be imported into a Unity project,
|
||||||
|
with the Version Handler component activating only the most recent version.
|
||||||
|
|
||||||
|
1. Create a manifest text file named `MY_UNIQUE_PLUGIN_NAME_VERSION.txt` that
|
||||||
|
lists all the files in your plugin relative to the project root. Then add
|
||||||
|
the `gvh_manifest` label to the asset to indicate this file is a plugin
|
||||||
|
manifest.
|
||||||
|
|
||||||
|
1. Optional: Add a `gvhp_manifestname-NAME` label to your manifest file to
|
||||||
|
provide a human readable name for your package. If this isn't provided the
|
||||||
|
name of the manifest file will be used as the package name. NAME can match
|
||||||
|
the pattern `[0-9]+[a-zA-Z -]` where a leading integer will set the priority
|
||||||
|
of the name where `0` is the highest priority and preferably used as the
|
||||||
|
display name. The lowest value (i.e highest priority name) will be used as
|
||||||
|
the display name and all other specified names will be aliases of the
|
||||||
|
display name. Aliases can refer to previous names of the package allowing
|
||||||
|
renaming across published versions.
|
||||||
|
|
||||||
|
1. Redistribute EDM4U Unity plugin with your plugin. See the
|
||||||
|
[Plugin Redistribution](#plugin-redistribution) section for details.
|
||||||
|
|
||||||
|
If you follow these steps:
|
||||||
|
|
||||||
|
* When users import a newer version of your plugin, files referenced by the
|
||||||
|
older version's manifest are cleaned up.
|
||||||
|
|
||||||
|
* The latest version of the plugin will be selected when users import multiple
|
||||||
|
packages that include your plugin, assuming the steps in
|
||||||
|
[Plugin Redistribution](#plugin-redistribution) are followed.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
Many Unity plugins have dependencies upon Android specific libraries, iOS
|
||||||
|
CocoaPods, and sometimes have transitive dependencies upon other Unity plugins.
|
||||||
|
This causes the following problems:
|
||||||
|
|
||||||
|
* Integrating platform specific (e.g Android and iOS) libraries within a Unity
|
||||||
|
project can be complex and a burden on a Unity plugin maintainer.
|
||||||
|
* The process of resolving conflicting dependencies on platform specific
|
||||||
|
libraries is pushed to the developer attempting to use a Unity plugin. The
|
||||||
|
developer trying to use your plugin is very likely to give up when faced
|
||||||
|
with Android or iOS specific build errors.
|
||||||
|
* The process of resolving conflicting Unity plugins (due to shared Unity
|
||||||
|
plugin components) is pushed to the developer attempting to use your Unity
|
||||||
|
plugin. In an effort to resolve conflicts, the developer will very likely
|
||||||
|
attempt to resolve problems by deleting random files in your plugin, report
|
||||||
|
bugs when that doesn't work and finally give up.
|
||||||
|
|
||||||
|
EDM4U provides solutions for each of these problems.
|
||||||
|
|
||||||
|
### Android Dependency Management
|
||||||
|
|
||||||
|
The *Android Resolver* component of this plugin will download and integrate
|
||||||
|
Android library dependencies and handle any conflicts between plugins that share
|
||||||
|
the same dependencies.
|
||||||
|
|
||||||
|
Without the Android Resolver, typically Unity plugins bundle their AAR and JAR
|
||||||
|
dependencies, e.g. a Unity plugin `SomePlugin` that requires the Google Play
|
||||||
|
Games Android library would redistribute the library and its transitive
|
||||||
|
dependencies in the folder `SomePlugin/Android/`. When a user imports
|
||||||
|
`SomeOtherPlugin` that includes the same libraries (potentially at different
|
||||||
|
versions) in `SomeOtherPlugin/Android/`, the developer using `SomePlugin` and
|
||||||
|
`SomeOtherPlugin` will see an error when building for Android that can be hard
|
||||||
|
to interpret.
|
||||||
|
|
||||||
|
Using the Android Resolver to manage Android library dependencies:
|
||||||
|
|
||||||
|
* Solves Android library conflicts between plugins.
|
||||||
|
* Handles all of the various processing steps required to use Android
|
||||||
|
libraries (AARs, JARs) in Unity 4.x and above projects. Almost all versions
|
||||||
|
of Unity have - at best - partial support for AARs.
|
||||||
|
* (Experimental) Supports minification of included Java components without
|
||||||
|
exporting a project.
|
||||||
|
|
||||||
|
### iOS Dependency Management
|
||||||
|
|
||||||
|
The *iOS Resolver* component of this plugin integrates with
|
||||||
|
[CocoaPods](https://cocoapods.org/) to download and integrate iOS libraries and
|
||||||
|
frameworks into the Xcode project Unity generates when building for iOS. Using
|
||||||
|
CocoaPods allows multiple plugins to utilize shared components without forcing
|
||||||
|
developers to fix either duplicate or incompatible versions of libraries
|
||||||
|
included through multiple Unity plugins in their project.
|
||||||
|
|
||||||
|
### Package Manager Registry Setup
|
||||||
|
|
||||||
|
The [Package Manager](https://docs.unity3d.com/Manual/Packages.html) (PM) makes
|
||||||
|
use of [NPM](https://www.npmjs.com/) registry servers for package hosting and
|
||||||
|
provides ways to discover, install, upgrade and uninstall packages. This makes
|
||||||
|
it easier for developers to manage plugins within their projects.
|
||||||
|
|
||||||
|
However, installing additional package registries requires a few manual steps
|
||||||
|
that can potentially be error prone. The *Package Manager Resolver* component of
|
||||||
|
this plugin integrates with [PM](https://docs.unity3d.com/Manual/Packages.html)
|
||||||
|
to provide a way to auto-install PM package registries when a `.unitypackage` is
|
||||||
|
installed which allows plugin maintainers to ship a `.unitypackage` that can
|
||||||
|
provide access to their own PM registry server to make it easier for developers
|
||||||
|
to manage their plugins.
|
||||||
|
|
||||||
|
### Unity Plugin Version Management
|
||||||
|
|
||||||
|
Finally, the *Version Handler* component of this plugin simplifies the process
|
||||||
|
of managing transitive dependencies of Unity plugins and each plugin's upgrade
|
||||||
|
process.
|
||||||
|
|
||||||
|
For example, without the Version Handler plugin, if:
|
||||||
|
|
||||||
|
* Unity plugin `SomePlugin` includes `EDM4U` plugin at version 1.1.
|
||||||
|
* Unity plugin `SomeOtherPlugin` includes `EDM4U` plugin at version 1.2.
|
||||||
|
|
||||||
|
The version of `EDM4U` included in the developer's project depends upon the
|
||||||
|
order the developer imports `SomePlugin` or `SomeOtherPlugin`.
|
||||||
|
|
||||||
|
This results in:
|
||||||
|
|
||||||
|
* `EDM4U` at version 1.2, if `SomePlugin` is imported then `SomeOtherPlugin`
|
||||||
|
is imported.
|
||||||
|
* `EDM4U` at version 1.1, if `SomeOtherPlugin` is imported then `SomePlugin`
|
||||||
|
is imported.
|
||||||
|
|
||||||
|
The Version Handler solves the problem of managing transitive dependencies by:
|
||||||
|
|
||||||
|
* Specifying a set of packaging requirements that enable a plugin at different
|
||||||
|
versions to be imported into a Unity project.
|
||||||
|
* Providing activation logic that selects the latest version of a plugin
|
||||||
|
within a project.
|
||||||
|
|
||||||
|
When using the Version Handler to manage `EDM4U` included in `SomePlugin` and
|
||||||
|
`SomeOtherPlugin`, from the prior example, version 1.2 will always be the
|
||||||
|
version activated in a developer's Unity project.
|
||||||
|
|
||||||
|
Plugin creators are encouraged to adopt this library to ease integration for
|
||||||
|
their customers. For more information about integrating EDM4U into your own
|
||||||
|
plugin, see the [Plugin Redistribution](#plugin-redistribution) section of this
|
||||||
|
document.
|
||||||
|
|
||||||
|
## Analytics
|
||||||
|
|
||||||
|
The External Dependency Manager for Unity plugin by default logs usage to Google
|
||||||
|
Analytics. The purpose of the logging is to quantitatively measure the usage of
|
||||||
|
functionality, to gather reports on integration failures and to inform future
|
||||||
|
improvements to the developer experience of the External Dependency Manager
|
||||||
|
plugin. Note that the analytics collected are limited to the scope of the EDM4U
|
||||||
|
plugin’s usage.
|
||||||
|
|
||||||
|
For details of what is logged, please refer to the usage of
|
||||||
|
`EditorMeasurement.Report()` in the source code.
|
||||||
|
|
||||||
|
## Plugin Redistribution
|
||||||
|
|
||||||
|
If you are a package maintainer and your package depends on EDM4U, it is highly
|
||||||
|
recommended to use the UPM format and add EDM4U as a dependency. If you must
|
||||||
|
include it in your `.unitypackage`, redistributing `EDM4U` inside your own
|
||||||
|
plugin might ease the integration process for your users.
|
||||||
|
|
||||||
|
If you wish to redistribute `EDM4U` inside your plugin, you **must** follow
|
||||||
|
these steps when importing the `external-dependency-manager-*.unitypackage`, and
|
||||||
|
when exporting your own plugin package:
|
||||||
|
|
||||||
|
1. Import the `external-dependency-manager-*.unitypackage` into your plugin
|
||||||
|
project by
|
||||||
|
[running Unity from the command line](https://docs.unity3d.com/Manual/CommandLineArguments.html),
|
||||||
|
ensuring that you add the `-gvh_disable` option.
|
||||||
|
1. Export your plugin by
|
||||||
|
[running Unity from the command line](https://docs.unity3d.com/Manual/CommandLineArguments.html),
|
||||||
|
ensuring that you:
|
||||||
|
- Include the contents of the `Assets/PlayServicesResolver` and
|
||||||
|
`Assets/ExternalDependencyManager` directory.
|
||||||
|
- Add the `-gvh_disable` option.
|
||||||
|
|
||||||
|
You **must** specify the `-gvh_disable` option in order for the Version Handler
|
||||||
|
to work correctly!
|
||||||
|
|
||||||
|
For example, the following command will import the
|
||||||
|
`external-dependency-manager-1.2.46.0.unitypackage` into the project
|
||||||
|
`MyPluginProject` and export the entire Assets folder to
|
||||||
|
`MyPlugin.unitypackage`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
Unity -gvh_disable \
|
||||||
|
-batchmode \
|
||||||
|
-importPackage external-dependency-manager-1.2.46.0.unitypackage \
|
||||||
|
-projectPath MyPluginProject \
|
||||||
|
-exportPackage Assets MyPlugin.unitypackage \
|
||||||
|
-quit
|
||||||
|
```
|
||||||
|
|
||||||
|
### Background
|
||||||
|
|
||||||
|
The *Version Handler* component relies upon deferring the load of editor DLLs so
|
||||||
|
that it can run first and determine the latest version of a plugin component to
|
||||||
|
activate. The build of `EDM4U` plugin has Unity asset metadata that is
|
||||||
|
configured so that the editor components are not initially enabled when it's
|
||||||
|
imported into a Unity project. To maintain this configuration when importing the
|
||||||
|
`external-dependency-manager.unitypackage` into a Unity plugin project, you
|
||||||
|
*must* specify the command line option `-gvh_disable` which will prevent the
|
||||||
|
Version Handler component from running and changing the Unity asset metadata.
|
||||||
|
|
||||||
|
## Building from Source
|
||||||
|
|
||||||
|
To build this plugin from source you need the following tools installed: * Unity
|
||||||
|
2021 and below (with iOS and Android modules installed) * Java 11
|
||||||
|
|
||||||
|
You can build the plugin by running the following from your shell (Linux / OSX):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./gradlew build
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
or Windows:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./gradlew.bat build
|
||||||
|
```
|
||||||
|
|
||||||
|
If Java 11 is not your default Java command, add
|
||||||
|
`-Dorg.gradle.java.home=<PATH_TO_JAVA_HOME>` to the command above.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
You can run the tests by running the following from your shell (Linux / OSX):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./gradlew test
|
||||||
|
```
|
||||||
|
|
||||||
|
or Windows:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./gradlew.bat test
|
||||||
|
```
|
||||||
|
|
||||||
|
The following properties can be set to narrow down the tests to run or change
|
||||||
|
the test run behavior.
|
||||||
|
|
||||||
|
* `INTERACTIVE_MODE_TESTS_ENABLED` - Default to `1`. Set to `1` to enable
|
||||||
|
interactive mode tests, which requires GPU on the machine. Otherwise, only
|
||||||
|
run tests in the batch mode.
|
||||||
|
* `INCLUDE_TEST_TYPES` - Default to empty string, which means to include every
|
||||||
|
type of the test. To narrow down the types of test to run, set this
|
||||||
|
properties with a list of case-insensitive type strings separated by comma.
|
||||||
|
For instance, `-PINCLUDE_TEST_TYPES="Python,NUnit"` means to include only
|
||||||
|
Python tests and NUnit tests. See `TestTypeEnum` in `build.gradle` for
|
||||||
|
available options.
|
||||||
|
* `EXCLUDE_TEST_TYPES` - Default to empty string, which means to exclude none.
|
||||||
|
To add types of tests to exclude, set this properties with a list of
|
||||||
|
case-insensitive type strings separated by comma. For instance,
|
||||||
|
`-PEXCLUDE_TEST_TYPES="Python,NUnit"` means to exclude Python tests and
|
||||||
|
NUnit tests. See `TestTypeEnum` in `build.gradle` for available options.
|
||||||
|
* `INCLUDE_TEST_MODULES` - Default to empty string, which means to include the
|
||||||
|
tests for every modules. To narrow down modules to test, set this properties
|
||||||
|
with a list of case-insensitive module strings separated by comma. For
|
||||||
|
instance, `-PINCLUDE_TEST_MODULES="Tool,AndroidResolver"` means to run tests
|
||||||
|
for tools and Android Resolver only. See `TestModuleEnum` in `build.gradle`
|
||||||
|
for available options.
|
||||||
|
* `EXCLUDE_TEST_MODULES` - Default to empty string, which means to exclude
|
||||||
|
none. To add modules to exclude, set this properties with a list of
|
||||||
|
case-insensitive module strings separated by comma. For instance,
|
||||||
|
`-PEXCLUDE_TEST_MODULES="Tool,AndroidResolver"` means to run tests for any
|
||||||
|
modules other than tools and Android Resolver. See `TestModuleEnum` in
|
||||||
|
`build.gradle` for available options.
|
||||||
|
* `EXCLUDE_TESTS` - Default to empty string, which means to exclude none. To
|
||||||
|
add tests to exclude, set this properties with a list of case-insensitive
|
||||||
|
test names separated by comma. For instance,
|
||||||
|
`-PEXCLUDE_TESTS="testGenGuids,testDownloadArtifacts"` means to run tests
|
||||||
|
except the tests with name of `testGenGuids` and `testDownloadArtifacts`.
|
||||||
|
* `CONTINUE_ON_FAIL_FOR_TESTS_ENABLED` - Default to `1`. Set to `1` to
|
||||||
|
continue running the next test when the current one fails. Otherwise, the
|
||||||
|
build script stops whenever any test fails.
|
||||||
|
|
||||||
|
For instance, by running the following command, it only runs the Unity
|
||||||
|
integration tests that does not requires GPU, but exclude tests for Android
|
||||||
|
Resolver module and iOS Resolver module.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./gradlew test \
|
||||||
|
-PINTERACTIVE_MODE_TESTS_ENABLED=0 \
|
||||||
|
-PINCLUDE_TEST_TYPES="Integration" \
|
||||||
|
-PEXCLUDE_TEST_MODULES="AndroidResolver,iOSResolver"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Releasing
|
||||||
|
|
||||||
|
Each time a new build of this plugin is checked into the source tree you need to
|
||||||
|
do the following:
|
||||||
|
|
||||||
|
* Bump the plugin version variable `pluginVersion` in `build.gradle`
|
||||||
|
* Update `CHANGELOG.md` with the new version number and changes included in
|
||||||
|
the release.
|
||||||
|
* Build the release using `./gradlew release` which performs the following:
|
||||||
|
* Updates `external-dependency-manager-*.unitypackage`
|
||||||
|
* Copies the unpacked plugin to the `exploded` directory.
|
||||||
|
* Updates template metadata files in the `plugin` directory. The GUIDs of
|
||||||
|
all asset metadata is modified due to the version number change. Each
|
||||||
|
file within the plugin is versioned to allow multiple versions of the
|
||||||
|
plugin to be imported into a Unity project which allows the most recent
|
||||||
|
version to be activated by the Version Handler component.
|
||||||
|
* Create release commit using `./gradlew gitCreateReleaseCommit` which
|
||||||
|
performs `git commit -a -m "description from CHANGELOG.md"`
|
||||||
|
* Once the release commit is merge, tag the release using `./gradlew
|
||||||
|
gitTagRelease` which performs the following:
|
||||||
|
* `git tag -a pluginVersion -m "version RELEASE"` to tag the release.
|
||||||
|
* Update tags on remote branch using `git push --tag REMOTE HEAD:master`
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 48c105d983344236ba7fd1e7d7208fca
|
||||||
|
labels:
|
||||||
|
- gvh
|
||||||
|
- gvh_version-1.2.183
|
||||||
|
- gvhp_exportpath-ExternalDependencyManager/Editor/README.md
|
||||||
|
timeCreated: 1584567712
|
||||||
|
licenseType: Store
|
||||||
|
TextScriptImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,13 @@
|
|||||||
|
Assets/ExternalDependencyManager/Editor/1.2.183/Google.IOSResolver.dll
|
||||||
|
Assets/ExternalDependencyManager/Editor/1.2.183/Google.IOSResolver.pdb
|
||||||
|
Assets/ExternalDependencyManager/Editor/1.2.183/Google.JarResolver.dll
|
||||||
|
Assets/ExternalDependencyManager/Editor/1.2.183/Google.JarResolver.pdb
|
||||||
|
Assets/ExternalDependencyManager/Editor/1.2.183/Google.PackageManagerResolver.dll
|
||||||
|
Assets/ExternalDependencyManager/Editor/1.2.183/Google.PackageManagerResolver.pdb
|
||||||
|
Assets/ExternalDependencyManager/Editor/1.2.183/Google.VersionHandlerImpl.dll
|
||||||
|
Assets/ExternalDependencyManager/Editor/1.2.183/Google.VersionHandlerImpl.pdb
|
||||||
|
Assets/ExternalDependencyManager/Editor/CHANGELOG.md
|
||||||
|
Assets/ExternalDependencyManager/Editor/Google.VersionHandler.dll
|
||||||
|
Assets/ExternalDependencyManager/Editor/Google.VersionHandler.pdb
|
||||||
|
Assets/ExternalDependencyManager/Editor/LICENSE
|
||||||
|
Assets/ExternalDependencyManager/Editor/README.md
|
@ -0,0 +1,15 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 98518d47e8b1473f8055106d5eb931c7
|
||||||
|
labels:
|
||||||
|
- gvh
|
||||||
|
- gvh_manifest
|
||||||
|
- gvh_version-1.2.183
|
||||||
|
- gvhp_exportpath-ExternalDependencyManager/Editor/external-dependency-manager_version-1.2.183_manifest.txt
|
||||||
|
- gvhp_manifestname-0External Dependency Manager
|
||||||
|
- gvhp_manifestname-play-services-resolver
|
||||||
|
timeCreated: 1474401009
|
||||||
|
licenseType: Store
|
||||||
|
TextScriptImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -31,7 +31,8 @@
|
|||||||
"GUID:dc960734dc080426fa6612f1c5fe95f3",
|
"GUID:dc960734dc080426fa6612f1c5fe95f3",
|
||||||
"GUID:66c2eb417c67ad849907d0769db96dbf",
|
"GUID:66c2eb417c67ad849907d0769db96dbf",
|
||||||
"GUID:2289059ddf1745b4d80a0f184af99d6b",
|
"GUID:2289059ddf1745b4d80a0f184af99d6b",
|
||||||
"GUID:448b0b55421917e4784a8f2f7449081f"
|
"GUID:448b0b55421917e4784a8f2f7449081f",
|
||||||
|
"GUID:bfb3a80268dac420ab25cd26e09e4475"
|
||||||
],
|
],
|
||||||
"includePlatforms": [],
|
"includePlatforms": [],
|
||||||
"excludePlatforms": [],
|
"excludePlatforms": [],
|
||||||
|
8
EintooAR/Assets/Plugins/VoxelBusters.meta
Normal file
8
EintooAR/Assets/Plugins/VoxelBusters.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d715a94341ea2409e8dc90d116c23ded
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
EintooAR/Assets/Plugins/VoxelBusters/CoreLibrary.meta
Normal file
8
EintooAR/Assets/Plugins/VoxelBusters/CoreLibrary.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5317c4912d76a48828be99991f62fe81
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2815bd3aec8ef4cdda0dd09e8ad737d2
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b00880799a9f24f7b8a4c2e44c84138e
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 36aa8feca9d884ed09e68d46a2a8a7b0
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,136 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
// Credits: https://github.com/joshcamas/unity-domain-reload-helper
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public static class DomainReloadManager
|
||||||
|
{
|
||||||
|
#region Static fields
|
||||||
|
|
||||||
|
private const BindingFlags kDefaultBindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Static methods
|
||||||
|
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||||
|
private static void OnRuntimeLoad()
|
||||||
|
{
|
||||||
|
TypeCache.Rebuild();
|
||||||
|
|
||||||
|
SendReloadMessageToMembers();
|
||||||
|
SendReloadMessageToMethods();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SendReloadMessageToMembers()
|
||||||
|
{
|
||||||
|
var members = TypeCache.GetMembersWithAttribute<ClearOnReloadAttribute>(
|
||||||
|
memberTypes: MemberTypes.Field | MemberTypes.Property | MemberTypes.Event,
|
||||||
|
bindingAttr: kDefaultBindingAttr);
|
||||||
|
|
||||||
|
foreach (var item in members)
|
||||||
|
{
|
||||||
|
var member = item.Key;
|
||||||
|
var attribute = item.Value;
|
||||||
|
if (member is FieldInfo)
|
||||||
|
{
|
||||||
|
ClearField(member as FieldInfo, attribute);
|
||||||
|
}
|
||||||
|
else if (member is PropertyInfo)
|
||||||
|
{
|
||||||
|
ClearProperty(member as PropertyInfo, attribute);
|
||||||
|
}
|
||||||
|
else if (member is EventInfo)
|
||||||
|
{
|
||||||
|
ClearEvent(member as EventInfo, attribute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SendReloadMessageToMethods()
|
||||||
|
{
|
||||||
|
foreach (var item in TypeCache.GetMethodsWithAttribute<ExecuteOnReloadAttribute>(kDefaultBindingAttr))
|
||||||
|
{
|
||||||
|
var methodInfo = item.Key;
|
||||||
|
if (methodInfo.IsGenericMethod) continue;
|
||||||
|
|
||||||
|
DebugLogger.Log(CoreLibraryDomain.Default, $"Invoking reload method: {methodInfo.Name}.");
|
||||||
|
methodInfo.Invoke(null, new object[] { });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ClearField(FieldInfo fieldInfo, ClearOnReloadAttribute clearAttribute)
|
||||||
|
{
|
||||||
|
var fieldType = fieldInfo.FieldType;
|
||||||
|
if (fieldType.IsGenericParameter || fieldInfo.DeclaringType.ContainsGenericParameters)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access attribute parameters
|
||||||
|
object resetValue = FindMemberResetValue(fieldType, clearAttribute);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DebugLogger.Log(CoreLibraryDomain.Default, $"Clearing field {fieldInfo.Name} to value {resetValue} for type {fieldType}.");
|
||||||
|
fieldInfo.SetValue(null, resetValue);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
DebugLogger.LogWarning(CoreLibraryDomain.Default, $"Unable to clear field {fieldInfo.Name}." + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ClearProperty(PropertyInfo propertyInfo, ClearOnReloadAttribute clearAttribute)
|
||||||
|
{
|
||||||
|
if (!propertyInfo.CanWrite) return;
|
||||||
|
|
||||||
|
// Access attribute parameters
|
||||||
|
var propertyType = propertyInfo.PropertyType;
|
||||||
|
object resetValue = FindMemberResetValue(propertyType, clearAttribute);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DebugLogger.Log(CoreLibraryDomain.Default, $"Clearing property {propertyInfo.Name} to value {resetValue}.");
|
||||||
|
propertyInfo.SetValue(null, resetValue);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
DebugLogger.LogWarning(CoreLibraryDomain.Default, $"Unable to clear property {propertyInfo.Name}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ClearEvent(EventInfo eventInfo, ClearOnReloadAttribute clearAttribute)
|
||||||
|
{
|
||||||
|
var fieldInfo = eventInfo.DeclaringType.GetField(eventInfo.Name, kDefaultBindingAttr);
|
||||||
|
if ((fieldInfo == null) || fieldInfo.FieldType.IsGenericParameter) return;
|
||||||
|
|
||||||
|
DebugLogger.Log(CoreLibraryDomain.Default, $"Clearing event {eventInfo.Name}.");
|
||||||
|
fieldInfo.SetValue(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object FindMemberResetValue(Type target, ClearOnReloadAttribute clearAttribute)
|
||||||
|
{
|
||||||
|
object finalValue = null;
|
||||||
|
if (ClearOnReloadOption.Custom == clearAttribute.Option)
|
||||||
|
{
|
||||||
|
finalValue = Convert.ChangeType(clearAttribute.CustomValue, target);
|
||||||
|
if (finalValue == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Unable to assign value of type {valueToAssign.GetType()} to field {field.Name} of type {fieldType}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (target.IsValueType || (ClearOnReloadOption.Default == clearAttribute.Option))
|
||||||
|
{
|
||||||
|
finalValue = Activator.CreateInstance(target);
|
||||||
|
}
|
||||||
|
return finalValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ec2658a60321e44fe81b86a95e54bd8f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,23 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public class SettingsObjectEditorManager
|
||||||
|
{
|
||||||
|
[InitializeOnLoadMethod]
|
||||||
|
private static void OnEditorReload()
|
||||||
|
{
|
||||||
|
var settingsObjects = AssetDatabaseUtility.FindAssetObjects<SettingsObject>();
|
||||||
|
foreach (var obj in settingsObjects)
|
||||||
|
{
|
||||||
|
if(obj != null)
|
||||||
|
{
|
||||||
|
obj.OnEditorReload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 668d41341e45a44ed8ea4d6145939d4b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1207f43c46f3944158e30563ec0a499c
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,228 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public static class CustomEditorStyles
|
||||||
|
{
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
public static Color BorderColor => new Color(0.15f, 0.15f, 0.15f, 1f);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Label styles
|
||||||
|
|
||||||
|
public static GUIStyle Heading1Label()
|
||||||
|
{
|
||||||
|
return CreateLabel(baseStyle: EditorStyles.boldLabel,
|
||||||
|
fontSize: 18,
|
||||||
|
wordWrap: true,
|
||||||
|
richText: true,
|
||||||
|
alignment: TextAnchor.MiddleLeft);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GUIStyle Heading2Label()
|
||||||
|
{
|
||||||
|
return CreateLabel(baseStyle: EditorStyles.boldLabel,
|
||||||
|
fontSize: 16,
|
||||||
|
wordWrap: true,
|
||||||
|
richText: true,
|
||||||
|
alignment: TextAnchor.MiddleLeft);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GUIStyle Heading3Label()
|
||||||
|
{
|
||||||
|
return CreateLabel(baseStyle: EditorStyles.boldLabel,
|
||||||
|
fontSize: 14,
|
||||||
|
wordWrap: true,
|
||||||
|
richText: true,
|
||||||
|
alignment: TextAnchor.MiddleLeft);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GUIStyle NormalLabel()
|
||||||
|
{
|
||||||
|
return CreateLabel(baseStyle: EditorStyles.label,
|
||||||
|
fontSize: 14,
|
||||||
|
wordWrap: true,
|
||||||
|
richText: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GUIStyle OptionsLabel(bool wordWrap = true)
|
||||||
|
{
|
||||||
|
return CreateLabel(baseStyle: EditorStyles.label,
|
||||||
|
fontSize: 12,
|
||||||
|
wordWrap: wordWrap,
|
||||||
|
richText: true,
|
||||||
|
textClipping: TextClipping.Clip);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GUIStyle LinkLabel()
|
||||||
|
{
|
||||||
|
return CreateLabel(baseStyle: "LinkLabel",
|
||||||
|
fontSize: 12,
|
||||||
|
wordWrap: true,
|
||||||
|
richText: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GUIStyle MiniLabel(bool wordWrap = true)
|
||||||
|
{
|
||||||
|
return CreateLabel(baseStyle: EditorStyles.miniLabel,
|
||||||
|
fontSize: 12,
|
||||||
|
wordWrap: wordWrap,
|
||||||
|
richText: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static GUIStyle SelectableLabel(FontStyle? fontStyle = null,
|
||||||
|
int? fontSize = null,
|
||||||
|
Color? textColor = null)
|
||||||
|
{
|
||||||
|
var normal = EditorStyles.label;
|
||||||
|
var baseStyle = new GUIStyle()
|
||||||
|
{
|
||||||
|
border = new RectOffset(0, 0, 0, 0),
|
||||||
|
font = normal.font,
|
||||||
|
alignment = normal.alignment,
|
||||||
|
active = normal.active,
|
||||||
|
onActive = normal.onActive,
|
||||||
|
focused = normal.focused,
|
||||||
|
onFocused = normal.onFocused,
|
||||||
|
normal = normal.normal,
|
||||||
|
onNormal = normal.onNormal,
|
||||||
|
hover = normal.hover,
|
||||||
|
onHover = normal.onHover,
|
||||||
|
};
|
||||||
|
return CreateLabel(baseStyle,
|
||||||
|
fontStyle: fontStyle,
|
||||||
|
fontSize: fontSize ?? 14,
|
||||||
|
textColor: textColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Button styles
|
||||||
|
|
||||||
|
public static GUIStyle Button()
|
||||||
|
{
|
||||||
|
return CreateButton(baseStyle: "Button",
|
||||||
|
fontSize: 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GUIStyle InvisibleButton()
|
||||||
|
{
|
||||||
|
return CreateButton(baseStyle: "InvisibleButton");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Background styles
|
||||||
|
|
||||||
|
public static GUIStyle ItemBackground()
|
||||||
|
{
|
||||||
|
return CreateBackground(baseStyle: "AnimItemBackground");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GUIStyle GroupBackground()
|
||||||
|
{
|
||||||
|
GUIStyle baseStyle = "FrameBox";
|
||||||
|
var baseMargin = baseStyle.margin;
|
||||||
|
return CreateBackground(baseStyle: baseStyle,
|
||||||
|
border: new RectOffset(0, 0, 0, 0),
|
||||||
|
margin: new RectOffset(baseMargin.left, baseMargin.right, baseMargin.top, 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Static methods
|
||||||
|
|
||||||
|
public static GUIStyle CreateLabel(GUIStyle baseStyle,
|
||||||
|
FontStyle? fontStyle = null,
|
||||||
|
int? fontSize = null,
|
||||||
|
Color? textColor = null,
|
||||||
|
bool? wordWrap = null,
|
||||||
|
bool? richText = null,
|
||||||
|
TextAnchor? alignment = null,
|
||||||
|
TextClipping? textClipping = null)
|
||||||
|
{
|
||||||
|
var newStyle = new GUIStyle(baseStyle);
|
||||||
|
if (fontSize != null)
|
||||||
|
{
|
||||||
|
newStyle.fontSize = fontSize.Value;
|
||||||
|
}
|
||||||
|
if (fontStyle != null)
|
||||||
|
{
|
||||||
|
newStyle.fontStyle = fontStyle.Value;
|
||||||
|
}
|
||||||
|
if (textColor != null)
|
||||||
|
{
|
||||||
|
newStyle.normal.textColor = textColor.Value;
|
||||||
|
newStyle.onNormal.textColor = textColor.Value;
|
||||||
|
newStyle.active.textColor = textColor.Value;
|
||||||
|
newStyle.onActive.textColor = textColor.Value;
|
||||||
|
newStyle.focused.textColor = textColor.Value;
|
||||||
|
newStyle.onFocused.textColor = textColor.Value;
|
||||||
|
newStyle.hover.textColor = textColor.Value;
|
||||||
|
newStyle.onHover.textColor = textColor.Value;
|
||||||
|
}
|
||||||
|
if (wordWrap != null)
|
||||||
|
{
|
||||||
|
newStyle.wordWrap = wordWrap.Value;
|
||||||
|
}
|
||||||
|
if (richText != null)
|
||||||
|
{
|
||||||
|
newStyle.richText = richText.Value;
|
||||||
|
}
|
||||||
|
if (alignment != null)
|
||||||
|
{
|
||||||
|
newStyle.alignment = alignment.Value;
|
||||||
|
}
|
||||||
|
if (textClipping != null)
|
||||||
|
{
|
||||||
|
newStyle.clipping = textClipping.Value;
|
||||||
|
}
|
||||||
|
return newStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GUIStyle CreateButton(GUIStyle baseStyle,
|
||||||
|
int? fontSize = null,
|
||||||
|
int? fixedHeight = null,
|
||||||
|
TextAnchor? alignment = null)
|
||||||
|
{
|
||||||
|
var newStyle = new GUIStyle(baseStyle);
|
||||||
|
if (fontSize != null)
|
||||||
|
{
|
||||||
|
newStyle.fontSize = fontSize.Value;
|
||||||
|
}
|
||||||
|
if (fixedHeight != null)
|
||||||
|
{
|
||||||
|
newStyle.fixedHeight = fixedHeight.Value;
|
||||||
|
}
|
||||||
|
if (alignment != null)
|
||||||
|
{
|
||||||
|
newStyle.alignment = alignment.Value;
|
||||||
|
}
|
||||||
|
return newStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GUIStyle CreateBackground(GUIStyle baseStyle,
|
||||||
|
RectOffset border = null,
|
||||||
|
RectOffset margin = null)
|
||||||
|
{
|
||||||
|
var newStyle = new GUIStyle(baseStyle);
|
||||||
|
if (border != null)
|
||||||
|
{
|
||||||
|
newStyle.border = border;
|
||||||
|
}
|
||||||
|
if (margin != null)
|
||||||
|
{
|
||||||
|
newStyle.margin = margin;
|
||||||
|
}
|
||||||
|
return newStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1e146cad3338c4e56a70c8554ed2901c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,52 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public class CustomInspector : UnityEditor.Editor
|
||||||
|
{
|
||||||
|
#region Unity methods
|
||||||
|
|
||||||
|
protected virtual void OnEnable()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
protected virtual void OnDisable()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
|
|
||||||
|
DrawCustomInspector();
|
||||||
|
|
||||||
|
bool isDirty = EditorGUI.EndChangeCheck();
|
||||||
|
if (isDirty || UnityEditorUtility.GetIsEditorDirty())
|
||||||
|
{
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
UnityEditorUtility.SetIsEditorDirty(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void DrawCustomInspector()
|
||||||
|
{
|
||||||
|
var property = serializedObject.GetIterator();
|
||||||
|
if (property.NextVisible(true))
|
||||||
|
{
|
||||||
|
if (property.name == "m_Script")
|
||||||
|
{
|
||||||
|
GUI.enabled = false;
|
||||||
|
EditorGUILayout.PropertyField(property, true);
|
||||||
|
GUI.enabled = true;
|
||||||
|
}
|
||||||
|
while (property.NextVisible(false))
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(property, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 71ff8e366aef840198f173c1b05160fc
|
||||||
|
timeCreated: 1579541126
|
||||||
|
licenseType: Store
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,527 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public class EditorLayoutBuilder
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
private SerializedObject m_serializedObject;
|
||||||
|
|
||||||
|
private string[] m_tabs;
|
||||||
|
|
||||||
|
private GetSectionsCallback m_getSectionsCallback;
|
||||||
|
|
||||||
|
private DrawCallback m_drawTopBarCallback;
|
||||||
|
|
||||||
|
private DrawTabViewCallback m_drawTabViewCallback;
|
||||||
|
|
||||||
|
private DrawCallback m_drawFooterCallback;
|
||||||
|
|
||||||
|
private string m_selectedTab;
|
||||||
|
|
||||||
|
private EditorSectionInfo[] m_selectedTabSections;
|
||||||
|
|
||||||
|
private EditorSectionInfo m_focusSection;
|
||||||
|
|
||||||
|
private Vector2 m_tabBarScrollPosition;
|
||||||
|
|
||||||
|
private Texture2D m_toggleOnIcon;
|
||||||
|
|
||||||
|
private Texture2D m_toggleOffIcon;
|
||||||
|
|
||||||
|
private GUIStyle m_backgroundStyle;
|
||||||
|
|
||||||
|
private GUIStyle m_titleLabelStyle;
|
||||||
|
|
||||||
|
private GUIStyle m_subtitleLabelStyle;
|
||||||
|
|
||||||
|
private GUIStyle m_tabBarLabelNormalStyle;
|
||||||
|
|
||||||
|
private GUIStyle m_tabBarLabelSelectedStyle;
|
||||||
|
|
||||||
|
private GUIStyle m_selectableLabelStyle;
|
||||||
|
|
||||||
|
private GUIStyle m_invisibleButtonStyle;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Delegates
|
||||||
|
|
||||||
|
public delegate EditorSectionInfo[] GetSectionsCallback(string tab);
|
||||||
|
|
||||||
|
public delegate void DrawCallback(string tab);
|
||||||
|
|
||||||
|
public delegate bool DrawTabViewCallback(string tab);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
|
||||||
|
public event Callback<EditorSectionInfo> OnSectionStatusChange;
|
||||||
|
|
||||||
|
public event Callback<EditorSectionInfo> OnFocusSectionValueChange;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public EditorLayoutBuilder(SerializedObject serializedObject,
|
||||||
|
string[] tabs,
|
||||||
|
GetSectionsCallback getSectionsCallback,
|
||||||
|
DrawCallback drawTopBarCallback,
|
||||||
|
DrawTabViewCallback drawTabViewCallback,
|
||||||
|
DrawCallback drawFooterCallback,
|
||||||
|
Texture2D toggleOnIcon,
|
||||||
|
Texture2D toggleOffIcon)
|
||||||
|
{
|
||||||
|
// Set properties
|
||||||
|
m_serializedObject = serializedObject;
|
||||||
|
m_tabs = tabs;
|
||||||
|
m_getSectionsCallback = getSectionsCallback;
|
||||||
|
m_drawTopBarCallback = drawTopBarCallback;
|
||||||
|
m_drawTabViewCallback = drawTabViewCallback;
|
||||||
|
m_drawFooterCallback = drawFooterCallback;
|
||||||
|
m_toggleOnIcon = toggleOnIcon;
|
||||||
|
m_toggleOffIcon = toggleOffIcon;
|
||||||
|
|
||||||
|
LoadStyles();
|
||||||
|
SetSelectedTab(m_tabs[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Static methods
|
||||||
|
|
||||||
|
public static void DrawChildProperties(SerializedProperty property,
|
||||||
|
string prefix = null,
|
||||||
|
bool indent = true,
|
||||||
|
params string[] ignoreProperties)
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginVertical(CustomEditorStyles.GroupBackground());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
float maxWidth = 0;
|
||||||
|
//Calculate max label width first
|
||||||
|
IterateThroughValidChildren(property, ignoreProperties, (eachChildProperty) =>
|
||||||
|
{
|
||||||
|
var currentDisplayName = (prefix != null) ? $"{prefix} {eachChildProperty.displayName}" : eachChildProperty.displayName;
|
||||||
|
float currentLabelWidth = GUI.skin.label.CalcSize(new GUIContent(currentDisplayName)).x;
|
||||||
|
|
||||||
|
if(maxWidth < currentLabelWidth)
|
||||||
|
{
|
||||||
|
maxWidth = currentLabelWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (indent)
|
||||||
|
{
|
||||||
|
EditorGUI.indentLevel++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldLabelWidth = EditorGUIUtility.labelWidth;
|
||||||
|
EditorGUIUtility.labelWidth = ((maxWidth / 128) + 1) * 128; //Adding some extra 128 pixels buffer.
|
||||||
|
IterateThroughValidChildren(property, ignoreProperties, (eachChildProperty) =>
|
||||||
|
{
|
||||||
|
// Display the property
|
||||||
|
if (prefix != null)
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(eachChildProperty, new GUIContent($"{prefix} {eachChildProperty.displayName}", eachChildProperty.tooltip), true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(eachChildProperty, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
EditorGUIUtility.labelWidth = oldLabelWidth;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (indent)
|
||||||
|
{
|
||||||
|
EditorGUI.indentLevel--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void IterateThroughValidChildren(SerializedProperty property, string[] ignoreProperties, Action<SerializedProperty> callbackOnEachChild)
|
||||||
|
{
|
||||||
|
// Move pointer to first element
|
||||||
|
var currentProperty = property.Copy();
|
||||||
|
var endProperty = default(SerializedProperty);
|
||||||
|
|
||||||
|
// Start iterating through the properties
|
||||||
|
bool firstTime = true;
|
||||||
|
while (currentProperty.NextVisible(enterChildren: firstTime))
|
||||||
|
{
|
||||||
|
if (firstTime)
|
||||||
|
{
|
||||||
|
endProperty = property.GetEndProperty();
|
||||||
|
firstTime = false;
|
||||||
|
}
|
||||||
|
if (SerializedProperty.EqualContents(currentProperty, endProperty))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude specified properties
|
||||||
|
if ((ignoreProperties != null) && System.Array.Exists(ignoreProperties, (item) => string.Equals(item, currentProperty.name)))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
callbackOnEachChild(currentProperty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public methods
|
||||||
|
|
||||||
|
public void DoLayout()
|
||||||
|
{
|
||||||
|
if (CanShowFocusSection())
|
||||||
|
{
|
||||||
|
DrawFocusSection();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_drawTopBarCallback?.Invoke(m_selectedTab);
|
||||||
|
DrawTabBar();
|
||||||
|
DrawTabView();
|
||||||
|
m_drawFooterCallback?.Invoke(m_selectedTab);
|
||||||
|
}
|
||||||
|
m_serializedObject.ApplyModifiedProperties();
|
||||||
|
m_serializedObject.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawSection(EditorSectionInfo section,
|
||||||
|
bool showDetails = true,
|
||||||
|
bool selectable = true)
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginVertical(m_backgroundStyle);
|
||||||
|
bool expanded = DrawSectionHeader(section,
|
||||||
|
selectable);
|
||||||
|
bool endGroup = true;
|
||||||
|
if (showDetails || expanded)
|
||||||
|
{
|
||||||
|
if (section.DrawStyle == EditorSectionDrawStyle.Default)
|
||||||
|
{
|
||||||
|
endGroup = false;
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section.DrawDetailsCallback != null)
|
||||||
|
{
|
||||||
|
section.DrawDetailsCallback(section);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (section.DrawStyle == EditorSectionDrawStyle.Default)
|
||||||
|
{
|
||||||
|
endGroup = true;
|
||||||
|
EditorGUILayout.BeginVertical(m_backgroundStyle);
|
||||||
|
}
|
||||||
|
DrawSectionDetails(section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (endGroup)
|
||||||
|
{
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawSectionDetails(EditorSectionInfo section)
|
||||||
|
{
|
||||||
|
bool originalGUIState = GUI.enabled;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Update edit capability
|
||||||
|
GUI.enabled = section.IsEnabled;
|
||||||
|
|
||||||
|
// Draw section properties
|
||||||
|
DrawChildProperties(property: section.Property,
|
||||||
|
ignoreProperties: section.IgnoreProperties);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Reset gui state
|
||||||
|
GUI.enabled = originalGUIState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeginVertical()
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginVertical(m_backgroundStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndVertical()
|
||||||
|
{
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private methods
|
||||||
|
|
||||||
|
private void LoadStyles()
|
||||||
|
{
|
||||||
|
m_backgroundStyle = CustomEditorStyles.GroupBackground();
|
||||||
|
m_titleLabelStyle = CustomEditorStyles.Heading2Label();
|
||||||
|
m_subtitleLabelStyle = CustomEditorStyles.OptionsLabel(wordWrap: false);
|
||||||
|
m_tabBarLabelNormalStyle = CustomEditorStyles.SelectableLabel(fontSize: 16, textColor: Color.gray);
|
||||||
|
m_tabBarLabelSelectedStyle = CustomEditorStyles.SelectableLabel(fontSize: 16, fontStyle: FontStyle.Bold);
|
||||||
|
m_selectableLabelStyle = CustomEditorStyles.SelectableLabel();
|
||||||
|
m_invisibleButtonStyle = CustomEditorStyles.InvisibleButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanShowFocusSection()
|
||||||
|
{
|
||||||
|
return (m_focusSection != null) && (m_focusSection.DrawStyle == EditorSectionDrawStyle.Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawTabBar()
|
||||||
|
{
|
||||||
|
if (m_tabs.Length > 1)
|
||||||
|
{
|
||||||
|
m_tabBarScrollPosition = GUILayout.BeginScrollView(m_tabBarScrollPosition, m_backgroundStyle, GUILayout.Height(30f));
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
for (int iter = 0; iter < m_tabs.Length; iter++)
|
||||||
|
{
|
||||||
|
var current = m_tabs[iter];
|
||||||
|
var labelStyle = (current == m_selectedTab) ? m_tabBarLabelSelectedStyle : m_tabBarLabelNormalStyle;
|
||||||
|
if (GUILayout.Button(current, labelStyle, GUILayout.Width(80f)))
|
||||||
|
{
|
||||||
|
SetSelectedTab(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
GUILayout.EndScrollView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawTabView()
|
||||||
|
{
|
||||||
|
if ((m_drawTabViewCallback == null) || !m_drawTabViewCallback(m_selectedTab))
|
||||||
|
{
|
||||||
|
for (int iter = 0; iter < m_selectedTabSections.Length; iter++)
|
||||||
|
{
|
||||||
|
var current = m_selectedTabSections[iter];
|
||||||
|
DrawSection(section: current,
|
||||||
|
showDetails: (current.DrawStyle == EditorSectionDrawStyle.Expand) && (current == m_focusSection));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DrawSectionHeader(EditorSectionInfo section,
|
||||||
|
bool selectable = true)
|
||||||
|
{
|
||||||
|
bool isSelected = (section == m_focusSection);
|
||||||
|
bool hasSubtitle = (section.Description != null);
|
||||||
|
float headerHeight = hasSubtitle ? 52f : 30f;
|
||||||
|
|
||||||
|
// Draw rect
|
||||||
|
var rect = EditorGUILayout.GetControlRect(false, headerHeight);
|
||||||
|
//GUI.Box(rect, GUIContent.none, HeaderButtonStyle);
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Draw expand control
|
||||||
|
if (drawStyle == PropertyGroupDrawStyle.Expand)
|
||||||
|
{
|
||||||
|
var foldOutRect = new Rect(rect.x + 5f, rect.y, 50f, rect.height);
|
||||||
|
EditorGUI.LabelField(foldOutRect, isSelected ? "-" : "+", CustomEditorStyles.Heading3);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Draw text
|
||||||
|
var titleRect = new Rect(rect.x + 5f,
|
||||||
|
rect.y + 4f,
|
||||||
|
rect.width * 0.8f,
|
||||||
|
22f);
|
||||||
|
EditorGUI.LabelField(titleRect, section.DisplayName, m_titleLabelStyle);
|
||||||
|
if (hasSubtitle)
|
||||||
|
{
|
||||||
|
var subtitleRect = new Rect(rect.x + 5f,
|
||||||
|
rect.y + 25f,
|
||||||
|
rect.width * 0.8f,
|
||||||
|
18f);
|
||||||
|
EditorGUI.LabelField(subtitleRect, section.Description, m_subtitleLabelStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw selectable rect
|
||||||
|
var selectableRect = new Rect(rect.x,
|
||||||
|
rect.y,
|
||||||
|
rect.width * 0.8f,
|
||||||
|
rect.height);
|
||||||
|
if (selectable && EditorLayoutUtility.TransparentButton(selectableRect))
|
||||||
|
{
|
||||||
|
isSelected = SetFocusSection(section);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw toggle button
|
||||||
|
var enabledProperty = section.EnabledProperty;
|
||||||
|
if (enabledProperty != null)
|
||||||
|
{
|
||||||
|
var toggleIcon = enabledProperty.boolValue ? m_toggleOnIcon : m_toggleOffIcon;
|
||||||
|
var iconSize = new Vector2(64f, 32f);
|
||||||
|
var toggleRect = new Rect(rect.xMax - (iconSize.x * 1.2f),
|
||||||
|
titleRect.yMin + ((titleRect.height - iconSize.y/2)),
|
||||||
|
iconSize.x,
|
||||||
|
iconSize.y);
|
||||||
|
if (GUI.Button(toggleRect, toggleIcon, m_invisibleButtonStyle))
|
||||||
|
{
|
||||||
|
enabledProperty.boolValue = !enabledProperty.boolValue;
|
||||||
|
|
||||||
|
// Raise an event to notify others, delay is added to ensure that modified properties are serialized
|
||||||
|
EditorApplication.delayCall += () => { OnSectionStatusChange?.Invoke(section); };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawFocusSection()
|
||||||
|
{
|
||||||
|
var property = m_focusSection.Property;
|
||||||
|
EditorGUILayout.BeginHorizontal(m_backgroundStyle);
|
||||||
|
if (GUILayout.Button($"{'\u2190'} Back To Main Menu", m_titleLabelStyle))
|
||||||
|
{
|
||||||
|
SetFocusSection(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
property.isExpanded = true;
|
||||||
|
DrawSection(section: m_focusSection,
|
||||||
|
selectable: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Getter methods
|
||||||
|
|
||||||
|
private EditorSectionInfo[] GetSections(string tab)
|
||||||
|
{
|
||||||
|
return m_getSectionsCallback(tab) ?? new EditorSectionInfo[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Setter methods
|
||||||
|
|
||||||
|
private void SetSelectedTab(string tab)
|
||||||
|
{
|
||||||
|
// Update cached data
|
||||||
|
m_selectedTab = tab;
|
||||||
|
m_selectedTabSections = GetSections(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SetFocusSection(EditorSectionInfo section)
|
||||||
|
{
|
||||||
|
var oldFocusSection = m_focusSection;
|
||||||
|
|
||||||
|
// Set new value
|
||||||
|
m_focusSection = section;
|
||||||
|
|
||||||
|
// Update selection state
|
||||||
|
if ((section != null) && (section == oldFocusSection))
|
||||||
|
{
|
||||||
|
section.Property.isExpanded = !section.Property.isExpanded;
|
||||||
|
m_focusSection = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (oldFocusSection != null)
|
||||||
|
{
|
||||||
|
oldFocusSection.Property.isExpanded = false;
|
||||||
|
}
|
||||||
|
if (section != null)
|
||||||
|
{
|
||||||
|
section.Property.isExpanded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool hasChanged = (oldFocusSection != m_focusSection);
|
||||||
|
if (hasChanged)
|
||||||
|
{
|
||||||
|
OnFocusSectionValueChange?.Invoke(m_focusSection);
|
||||||
|
}
|
||||||
|
return hasChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EditorSectionInfo
|
||||||
|
{
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
public string DisplayName { get; private set; }
|
||||||
|
|
||||||
|
public string Description { get; private set; }
|
||||||
|
|
||||||
|
public bool IsEnabled => (EnabledProperty == null) || EnabledProperty.boolValue;
|
||||||
|
|
||||||
|
public SerializedProperty Property { get; private set; }
|
||||||
|
|
||||||
|
public SerializedProperty EnabledProperty { get; private set; }
|
||||||
|
|
||||||
|
public EditorSectionDrawStyle DrawStyle { get; private set; }
|
||||||
|
|
||||||
|
public DrawCallback DrawDetailsCallback { get; private set; }
|
||||||
|
|
||||||
|
public string[] IgnoreProperties { get; private set; }
|
||||||
|
|
||||||
|
public object Tag { get; private set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Delegates
|
||||||
|
|
||||||
|
public delegate void DrawCallback(EditorSectionInfo section);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public EditorSectionInfo(string displayName,
|
||||||
|
SerializedProperty property,
|
||||||
|
SerializedProperty enabledProperty = null,
|
||||||
|
string description = null,
|
||||||
|
EditorSectionDrawStyle drawStyle = EditorSectionDrawStyle.Default,
|
||||||
|
DrawCallback drawDetailsCallback = null,
|
||||||
|
object tag = null,
|
||||||
|
params string[] ignoreProperties)
|
||||||
|
{
|
||||||
|
Assert.IsArgNotNull(displayName, nameof(displayName));
|
||||||
|
Assert.IsArgNotNull(property, nameof(property));
|
||||||
|
|
||||||
|
// set properties
|
||||||
|
DisplayName = displayName;
|
||||||
|
Description = description;
|
||||||
|
Property = property;
|
||||||
|
EnabledProperty = enabledProperty ?? property.FindPropertyRelative("m_isEnabled");
|
||||||
|
DrawStyle = drawStyle;
|
||||||
|
DrawDetailsCallback = drawDetailsCallback;
|
||||||
|
IgnoreProperties = ignoreProperties ?? new string[] { "m_isEnabled" };
|
||||||
|
Tag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum EditorSectionDrawStyle
|
||||||
|
{
|
||||||
|
Default,
|
||||||
|
|
||||||
|
Expand,
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4019dad4610704cb38751061068b7fa8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,117 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public static class EditorLayoutUtility
|
||||||
|
{
|
||||||
|
#region Static methods
|
||||||
|
|
||||||
|
public static void DisableGUI()
|
||||||
|
{
|
||||||
|
GUI.enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EnableGUI()
|
||||||
|
{
|
||||||
|
GUI.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TransparentButton(Rect rect,
|
||||||
|
string label = "")
|
||||||
|
{
|
||||||
|
var originalColor = GUI.color;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GUI.color = Color.clear;
|
||||||
|
return GUI.Button(rect, label);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
GUI.color = originalColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void StringPopup(int selectedIndex,
|
||||||
|
string[] options,
|
||||||
|
Callback<int> onValueChange,
|
||||||
|
params GUILayoutOption[] layoutOptions)
|
||||||
|
{
|
||||||
|
int newValue = EditorGUILayout.Popup(selectedIndex, options, layoutOptions);
|
||||||
|
if (newValue != selectedIndex)
|
||||||
|
{
|
||||||
|
onValueChange?.Invoke(newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void StringPopup(SerializedProperty stringProperty,
|
||||||
|
ref int selectedIndex,
|
||||||
|
string[] displayedOptions,
|
||||||
|
Callback<int> onValueChange = null,
|
||||||
|
params GUILayoutOption[] layoutOptions)
|
||||||
|
{
|
||||||
|
int newValue = EditorGUILayout.Popup(new GUIContent(stringProperty.displayName, stringProperty.tooltip),
|
||||||
|
selectedIndex,
|
||||||
|
displayedOptions,
|
||||||
|
layoutOptions);
|
||||||
|
if (newValue != selectedIndex)
|
||||||
|
{
|
||||||
|
selectedIndex = newValue;
|
||||||
|
stringProperty.stringValue = displayedOptions[newValue];
|
||||||
|
|
||||||
|
onValueChange?.Invoke(newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Helpbox(string title,
|
||||||
|
string description,
|
||||||
|
System.Action drawFunc,
|
||||||
|
GUIStyle style)
|
||||||
|
{
|
||||||
|
GUILayout.BeginVertical(style);
|
||||||
|
GUILayout.Label(title, EditorStyles.boldLabel);
|
||||||
|
GUILayout.Label(description, EditorStyles.wordWrappedLabel);
|
||||||
|
drawFunc();
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Helpbox(string title,
|
||||||
|
string description,
|
||||||
|
string actionLabel,
|
||||||
|
System.Action onClick,
|
||||||
|
GUIStyle style)
|
||||||
|
{
|
||||||
|
Helpbox(
|
||||||
|
title: title,
|
||||||
|
description: description,
|
||||||
|
drawFunc: () =>
|
||||||
|
{
|
||||||
|
if (GUILayout.Button(actionLabel))
|
||||||
|
{
|
||||||
|
onClick?.Invoke();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: style);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void BeginBottomBar(Color? borderColor = null,
|
||||||
|
params GUILayoutOption[] options)
|
||||||
|
{
|
||||||
|
var bottomBarRect = EditorGUILayout.BeginHorizontal(options);
|
||||||
|
bottomBarRect.height = 1f;
|
||||||
|
if (borderColor != null)
|
||||||
|
{
|
||||||
|
EditorGUI.DrawRect(bottomBarRect, borderColor.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EndBottomBar()
|
||||||
|
{
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c40767cd6fb464f3b83f7e68d5e47449
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,185 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public class EditorSplitView
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
private SplitDirection m_direction;
|
||||||
|
|
||||||
|
private float m_splitRatio;
|
||||||
|
|
||||||
|
private bool m_resize;
|
||||||
|
|
||||||
|
private Vector2 m_firstContainerScrollPos;
|
||||||
|
|
||||||
|
private Rect m_availableRect;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
private EditorSplitView(SplitDirection direction, float splitRatio)
|
||||||
|
{
|
||||||
|
m_splitRatio = splitRatio;
|
||||||
|
m_direction = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Static methods
|
||||||
|
|
||||||
|
public static EditorSplitView CreateHorizontalSplitView(float splitRatio = 0.3f)
|
||||||
|
{
|
||||||
|
return new EditorSplitView(SplitDirection.Horizontal, splitRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EditorSplitView CreateVerticalSplitView(float splitRatio = 0.3f)
|
||||||
|
{
|
||||||
|
return new EditorSplitView(SplitDirection.Vertical, splitRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private methods
|
||||||
|
|
||||||
|
public void BeginArea()
|
||||||
|
{
|
||||||
|
Rect tempRect = Rect.zero;
|
||||||
|
if (m_direction == SplitDirection.Horizontal)
|
||||||
|
{
|
||||||
|
tempRect = EditorGUILayout.BeginHorizontal();
|
||||||
|
}
|
||||||
|
else if (m_direction == SplitDirection.Vertical)
|
||||||
|
{
|
||||||
|
tempRect = EditorGUILayout.BeginVertical();
|
||||||
|
}
|
||||||
|
if (tempRect.width > 0.0f)
|
||||||
|
{
|
||||||
|
m_availableRect = tempRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
BeginContainer();
|
||||||
|
if (m_direction == SplitDirection.Horizontal)
|
||||||
|
{
|
||||||
|
m_firstContainerScrollPos = EditorGUILayout.BeginScrollView(m_firstContainerScrollPos, GUILayout.Width(m_availableRect.width * m_splitRatio));
|
||||||
|
}
|
||||||
|
else if (m_direction == SplitDirection.Vertical)
|
||||||
|
{
|
||||||
|
m_firstContainerScrollPos = EditorGUILayout.BeginScrollView(m_firstContainerScrollPos, GUILayout.Height(m_availableRect.height * m_splitRatio));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Split()
|
||||||
|
{
|
||||||
|
// firstly mark that first container has ended
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
EndContainer();
|
||||||
|
ResizeFirstContainer();
|
||||||
|
|
||||||
|
// initiate rendering second container
|
||||||
|
BeginContainer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndArea()
|
||||||
|
{
|
||||||
|
EndContainer();
|
||||||
|
|
||||||
|
if (m_direction == SplitDirection.Horizontal)
|
||||||
|
{
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
else if (m_direction == SplitDirection.Vertical)
|
||||||
|
{
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Rect BeginContainer()
|
||||||
|
{
|
||||||
|
if (m_direction == SplitDirection.Horizontal)
|
||||||
|
{
|
||||||
|
return EditorGUILayout.BeginVertical();
|
||||||
|
}
|
||||||
|
else if (m_direction == SplitDirection.Vertical)
|
||||||
|
{
|
||||||
|
return EditorGUILayout.BeginHorizontal();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Rect.zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EndContainer()
|
||||||
|
{
|
||||||
|
if (m_direction == SplitDirection.Horizontal)
|
||||||
|
{
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
else if (m_direction == SplitDirection.Vertical)
|
||||||
|
{
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResizeFirstContainer()
|
||||||
|
{
|
||||||
|
Rect resizeHandleRect;
|
||||||
|
if (m_direction == SplitDirection.Horizontal)
|
||||||
|
{
|
||||||
|
resizeHandleRect = new Rect(m_availableRect.width * m_splitRatio, m_availableRect.y, 1f, m_availableRect.height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resizeHandleRect = new Rect(m_availableRect.x, m_availableRect.height * m_splitRatio, m_availableRect.width, 1f);
|
||||||
|
}
|
||||||
|
EditorGUI.DrawRect(resizeHandleRect, CustomEditorStyles.BorderColor);
|
||||||
|
if (m_direction == SplitDirection.Horizontal)
|
||||||
|
{
|
||||||
|
EditorGUIUtility.AddCursorRect(resizeHandleRect, MouseCursor.ResizeHorizontal);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorGUIUtility.AddCursorRect(resizeHandleRect, MouseCursor.ResizeVertical);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Event.current.type == EventType.MouseDown && resizeHandleRect.Contains(Event.current.mousePosition))
|
||||||
|
{
|
||||||
|
m_resize = true;
|
||||||
|
}
|
||||||
|
if (m_resize)
|
||||||
|
{
|
||||||
|
if (m_direction == SplitDirection.Horizontal)
|
||||||
|
{
|
||||||
|
m_splitRatio = Event.current.mousePosition.x / m_availableRect.width;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_splitRatio = Event.current.mousePosition.y / m_availableRect.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Event.current.type == EventType.MouseUp)
|
||||||
|
{
|
||||||
|
m_resize = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Nested types
|
||||||
|
|
||||||
|
private enum SplitDirection
|
||||||
|
{
|
||||||
|
Horizontal,
|
||||||
|
|
||||||
|
Vertical
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a20197bdbee5a44b196d137503bca990
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,41 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public class EditorWindowZ : EditorWindow
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
[System.NonSerialized]
|
||||||
|
private bool m_isInitialized;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Unity methods
|
||||||
|
|
||||||
|
protected virtual void OnGUI()
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private methods
|
||||||
|
|
||||||
|
protected virtual void Init()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
protected void EnsureInitialized()
|
||||||
|
{
|
||||||
|
if (m_isInitialized) return;
|
||||||
|
|
||||||
|
m_isInitialized = true;
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a0757cac8d21f499e80db0e955801000
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using VoxelBusters.CoreLibrary;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
[CustomPropertyDrawer(typeof(EnumMaskFieldAttribute))]
|
||||||
|
public class EnumMaskFieldDrawer : PropertyDrawer
|
||||||
|
{
|
||||||
|
#region Drawer Methods
|
||||||
|
|
||||||
|
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||||
|
{
|
||||||
|
label = EditorGUI.BeginProperty(position, label, property);
|
||||||
|
if (IsEnum())
|
||||||
|
{
|
||||||
|
UnityEditorUtility.EnumFlagsField(position, label, property, GetEnumType());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
base.OnGUI(position, property, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUI.EndProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private methods
|
||||||
|
|
||||||
|
private Type GetEnumType()
|
||||||
|
{
|
||||||
|
return ((EnumMaskFieldAttribute)attribute).EnumType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsEnum()
|
||||||
|
{
|
||||||
|
Type type = GetEnumType();
|
||||||
|
#if NETFX_CORE
|
||||||
|
return type.GetTypeInfo().IsEnum;
|
||||||
|
#else
|
||||||
|
return type.IsEnum;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 11e33d767c33a4a10bc1e378ed17ef72
|
||||||
|
timeCreated: 1579074419
|
||||||
|
licenseType: Store
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,52 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using VoxelBusters.CoreLibrary;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
[CustomPropertyDrawer(typeof(FileBrowserAttribute))]
|
||||||
|
public class FileBrowserAttributeDrawer : PropertyDrawer
|
||||||
|
{
|
||||||
|
#region Base class methods
|
||||||
|
|
||||||
|
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||||
|
{
|
||||||
|
EditorGUI.BeginProperty(position, label, property);
|
||||||
|
|
||||||
|
// draw property
|
||||||
|
var valueRect = new Rect(position.x, position.y, position.width - 50, position.height);
|
||||||
|
var buttonRect = new Rect(position.xMax - 45, position.y, 45, position.height);
|
||||||
|
|
||||||
|
EditorGUI.PropertyField(valueRect, property, label);
|
||||||
|
if (GUI.Button(buttonRect, new GUIContent("Select")))
|
||||||
|
{
|
||||||
|
EditorApplication.delayCall += () =>
|
||||||
|
{
|
||||||
|
OpenFileBrowser(property);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUI.EndProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private methods
|
||||||
|
|
||||||
|
private void OpenFileBrowser(SerializedProperty property)
|
||||||
|
{
|
||||||
|
var value = EditorUtility.OpenFilePanel("Select file", IOServices.GetAbsolutePath(property.stringValue), ((FileBrowserAttribute)attribute).Extension);
|
||||||
|
if (!string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
property.stringValue = ((FileBrowserAttribute)attribute).UsesRelativePath ? IOServices.GetRelativePath(IOServices.GetAbsolutePath(""), value) : value;
|
||||||
|
UnityEditorUtility.SetIsEditorDirty(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f924f934566204936b517248f5f8f4bd
|
||||||
|
timeCreated: 1576323713
|
||||||
|
licenseType: Store
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,51 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using VoxelBusters.CoreLibrary;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
[CustomPropertyDrawer(typeof(FolderBrowserAttribute))]
|
||||||
|
public class FolderBrowserAttributeDrawer : PropertyDrawer
|
||||||
|
{
|
||||||
|
#region Base class methods
|
||||||
|
|
||||||
|
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||||
|
{
|
||||||
|
EditorGUI.BeginProperty(position, label, property);
|
||||||
|
|
||||||
|
// draw property
|
||||||
|
var valueRect = new Rect(position.x, position.y, position.width - 50, position.height);
|
||||||
|
var buttonRect = new Rect(position.xMax - 45, position.y, 45, position.height);
|
||||||
|
|
||||||
|
EditorGUI.PropertyField(valueRect, property, label);
|
||||||
|
if (GUI.Button(buttonRect, new GUIContent("Select")))
|
||||||
|
{
|
||||||
|
EditorApplication.delayCall += () =>
|
||||||
|
{
|
||||||
|
OpenFolderBrowser(property);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUI.EndProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private methods
|
||||||
|
|
||||||
|
private void OpenFolderBrowser(SerializedProperty property)
|
||||||
|
{
|
||||||
|
var value = EditorUtility.OpenFolderPanel("Select folder", IOServices.GetAbsolutePath(property.stringValue), string.Empty);
|
||||||
|
if (!string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
property.stringValue = ((FolderBrowserAttribute)attribute).UsesRelativePath ? IOServices.GetRelativePath(IOServices.GetAbsolutePath(""), value) : value;
|
||||||
|
UnityEditorUtility.SetIsEditorDirty(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cafbc9653f53348559d4e088e2014b44
|
||||||
|
timeCreated: 1576323712
|
||||||
|
licenseType: Store
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,38 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using VoxelBusters.CoreLibrary;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
[CustomPropertyDrawer(typeof(InterfaceFieldAttribute))]
|
||||||
|
public class InterfaceFieldAttributeDrawer : PropertyDrawer
|
||||||
|
{
|
||||||
|
#region Base class methods
|
||||||
|
|
||||||
|
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||||
|
{
|
||||||
|
EditorGUI.BeginProperty(position, label, property);
|
||||||
|
|
||||||
|
// draw property
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
|
EditorGUI.PropertyField(position, property, label);
|
||||||
|
if (EditorGUI.EndChangeCheck())
|
||||||
|
{
|
||||||
|
var reference = property.objectReferenceValue;
|
||||||
|
var interfaceType = ((InterfaceFieldAttribute)attribute).InterfaceType;
|
||||||
|
if (reference && !interfaceType.IsAssignableFrom(reference.GetType()))
|
||||||
|
{
|
||||||
|
DebugLogger.LogError(CoreLibraryDomain.Default, $"Object does not implement interface of type: {interfaceType}.");
|
||||||
|
property.objectReferenceValue = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUI.EndProperty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9c5bbe7cdfcd444908de802817480fbf
|
||||||
|
timeCreated: 1577514657
|
||||||
|
licenseType: Store
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,66 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
[CustomPropertyDrawer(typeof(ReadOnlyAttribute), true)]
|
||||||
|
public class ReadOnlyAttributeDrawer : PropertyDrawer
|
||||||
|
{
|
||||||
|
private const float m_padding = 10f;
|
||||||
|
|
||||||
|
#region Base class methods
|
||||||
|
|
||||||
|
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||||
|
{
|
||||||
|
var offset = GetMessageHeight();
|
||||||
|
var fieldHeight = EditorGUI.GetPropertyHeight(property, label, true);
|
||||||
|
var fieldRect = new Rect(position.x, position.y + m_padding, position.width, fieldHeight);
|
||||||
|
var messageRect = new Rect(position.x, position.y + fieldHeight + m_padding + 2f, position.width, offset);
|
||||||
|
|
||||||
|
GUI.enabled = false;
|
||||||
|
EditorGUI.PropertyField(fieldRect, property, label, true);
|
||||||
|
if (HasMessage())
|
||||||
|
{
|
||||||
|
EditorGUI.LabelField(messageRect, GetMessage(), GetMessageStyle());
|
||||||
|
}
|
||||||
|
GUI.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||||
|
{
|
||||||
|
return EditorGUI.GetPropertyHeight(property, label, true) + GetMessageHeight() + m_padding * 2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Utility methods
|
||||||
|
|
||||||
|
private GUIStyle GetMessageStyle()
|
||||||
|
{
|
||||||
|
return CustomEditorStyles.MiniLabel(wordWrap: false); //TODO: Using false as calcSize is not working properly with wordwrap on.
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasMessage()
|
||||||
|
{
|
||||||
|
return !string.IsNullOrEmpty(GetMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetMessage()
|
||||||
|
{
|
||||||
|
var message = ((ReadOnlyAttribute)attribute).Message;
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float GetMessageHeight()
|
||||||
|
{
|
||||||
|
var message = GetMessage();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(message))
|
||||||
|
return 0f;
|
||||||
|
|
||||||
|
return GetMessageStyle().CalcSize(new GUIContent(message)).y;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fb5b59ba618cd4d3a97f3f6a1f7ec0c6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,38 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using VoxelBusters.CoreLibrary;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
[CustomPropertyDrawer(typeof(RuntimePlatformConstant))]
|
||||||
|
public class RuntimePlatformConstantDrawer : PropertyDrawer
|
||||||
|
{
|
||||||
|
#region Unity methods
|
||||||
|
|
||||||
|
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||||
|
{
|
||||||
|
return EditorGUIUtility.singleLineHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||||
|
{
|
||||||
|
// show property name label
|
||||||
|
label = EditorGUI.BeginProperty(position, label, property);
|
||||||
|
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
|
||||||
|
|
||||||
|
// show property attributes
|
||||||
|
Rect platformRect = new Rect(position.x, position.y, 60f, position.height);
|
||||||
|
Rect idRect = new Rect(position.x + 65f, position.y, position.width - 65f, position.height);
|
||||||
|
int indentLevel = EditorGUI.indentLevel;
|
||||||
|
|
||||||
|
EditorGUI.indentLevel = 0;
|
||||||
|
EditorGUI.PropertyField(platformRect, property.FindPropertyRelative("m_platform"), GUIContent.none);
|
||||||
|
EditorGUI.PropertyField(idRect, property.FindPropertyRelative("m_value"), GUIContent.none);
|
||||||
|
EditorGUI.indentLevel = indentLevel;
|
||||||
|
|
||||||
|
EditorGUI.EndProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f8345dac92c6c4d8e813c3a6cc3bd585
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,244 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public abstract class SettingsObjectInspector : UnityEditor.Editor
|
||||||
|
{
|
||||||
|
#region Constants
|
||||||
|
|
||||||
|
private static readonly ButtonMeta[] s_emptyButtonArray = new ButtonMeta[0];
|
||||||
|
|
||||||
|
private static readonly string[] s_ignoredProperties = new string[] { "m_Script" };
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
private string m_productName;
|
||||||
|
|
||||||
|
private string m_productVersion;
|
||||||
|
|
||||||
|
// Assets
|
||||||
|
private Texture2D m_logoIcon;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
public EditorLayoutBuilder LayoutBuilder { get; private set; }
|
||||||
|
|
||||||
|
protected GUIStyle CustomMarginStyle { get; private set; }
|
||||||
|
|
||||||
|
protected GUIStyle GroupBackgroundStyle { get; private set; }
|
||||||
|
|
||||||
|
protected GUIStyle ProductNameStyle { get; private set; }
|
||||||
|
|
||||||
|
protected GUIStyle NormalLabelStyle { get; private set; }
|
||||||
|
|
||||||
|
protected GUIStyle OptionsLabelStyle { get; private set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Abstract methods
|
||||||
|
|
||||||
|
protected abstract UnityPackageDefinition GetOwner();
|
||||||
|
|
||||||
|
protected abstract string[] GetTabNames();
|
||||||
|
|
||||||
|
protected abstract EditorSectionInfo[] GetSectionsForTab(string tab);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Unity methods
|
||||||
|
|
||||||
|
protected virtual void OnEnable()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
EnsurePropertiesAreSet();
|
||||||
|
|
||||||
|
EditorGUILayout.BeginVertical(CustomMarginStyle);
|
||||||
|
LayoutBuilder.DoLayout();
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool UseDefaultMargins()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Draw methods
|
||||||
|
|
||||||
|
protected virtual void DrawTopBar(string tab)
|
||||||
|
{
|
||||||
|
GUILayout.BeginHorizontal(GroupBackgroundStyle);
|
||||||
|
|
||||||
|
// logo section
|
||||||
|
GUILayout.BeginVertical();
|
||||||
|
GUILayout.Space(2f);
|
||||||
|
GUILayout.Label(m_logoIcon, GUILayout.Height(64f), GUILayout.Width(64f));
|
||||||
|
GUILayout.Space(2f);
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
|
||||||
|
// product info
|
||||||
|
GUILayout.BeginVertical();
|
||||||
|
GUILayout.Label(m_productName, ProductNameStyle);
|
||||||
|
GUILayout.Label(m_productVersion, NormalLabelStyle);
|
||||||
|
GUILayout.Label("Copyright © 2024 Voxel Busters Interactive LLP.", OptionsLabelStyle);
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual bool DrawTabView(string tab)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void DrawFooter(string tab)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
protected virtual void DrawButtonList(ButtonMeta[] buttons)
|
||||||
|
{
|
||||||
|
GUILayout.BeginVertical();
|
||||||
|
foreach (var item in buttons)
|
||||||
|
{
|
||||||
|
if (GUILayout.Button(item.Label, GUILayout.MinHeight(80f)))
|
||||||
|
{
|
||||||
|
item?.OnClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private methods
|
||||||
|
|
||||||
|
private void EnsurePropertiesAreSet()
|
||||||
|
{
|
||||||
|
if (LayoutBuilder != null) return;
|
||||||
|
|
||||||
|
LoadCustomStyles();
|
||||||
|
LoadAssets();
|
||||||
|
|
||||||
|
// Set properties
|
||||||
|
var commonResourcePath = CoreLibrarySettings.Package.GetEditorResourcesPath();
|
||||||
|
var ownerPackage = GetOwner();
|
||||||
|
m_productName = ownerPackage.DisplayName;
|
||||||
|
m_productVersion = $"v{ownerPackage.Version}";
|
||||||
|
LayoutBuilder = new EditorLayoutBuilder(serializedObject: serializedObject,
|
||||||
|
tabs: GetTabNames(),
|
||||||
|
getSectionsCallback: GetSectionsForTab,
|
||||||
|
drawTopBarCallback: DrawTopBar,
|
||||||
|
drawTabViewCallback: DrawTabView,
|
||||||
|
drawFooterCallback: DrawFooter,
|
||||||
|
toggleOnIcon: AssetDatabase.LoadAssetAtPath<Texture2D>(commonResourcePath + "/Textures/toggle-on.png"),
|
||||||
|
toggleOffIcon: AssetDatabase.LoadAssetAtPath<Texture2D>(commonResourcePath + "/Textures/toggle-off.png"));
|
||||||
|
LayoutBuilder.OnSectionStatusChange += OnSectionStatusChange;
|
||||||
|
LayoutBuilder.OnFocusSectionValueChange += OnFocusSectionValueChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadCustomStyles()
|
||||||
|
{
|
||||||
|
CustomMarginStyle = new GUIStyle(EditorStyles.inspectorFullWidthMargins)
|
||||||
|
{
|
||||||
|
margin = new RectOffset(2, 2, 0, 0),
|
||||||
|
};
|
||||||
|
GroupBackgroundStyle = CustomEditorStyles.GroupBackground();
|
||||||
|
ProductNameStyle = CustomEditorStyles.Heading1Label();
|
||||||
|
NormalLabelStyle = CustomEditorStyles.NormalLabel();
|
||||||
|
OptionsLabelStyle = CustomEditorStyles.OptionsLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadAssets()
|
||||||
|
{
|
||||||
|
// load custom assets
|
||||||
|
var ownerResourcePath = GetOwner().GetEditorResourcesPath();
|
||||||
|
m_logoIcon = AssetDatabase.LoadAssetAtPath<Texture2D>(ownerResourcePath + "/Textures/logo.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void EnsureChangesAreSerialized()
|
||||||
|
{
|
||||||
|
EditorUtility.SetDirty(target);
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
serializedObject.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void TryApplyModifiedProperties()
|
||||||
|
{
|
||||||
|
if (EditorGUI.EndChangeCheck())
|
||||||
|
{
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
serializedObject.Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Misc methods
|
||||||
|
#if NATIVE_PLUGINS_SHOW_UPM_MIGRATION
|
||||||
|
protected void ShowMigrateToUpmOption()
|
||||||
|
{
|
||||||
|
EditorLayoutUtility.Helpbox(title: "UPM Support",
|
||||||
|
description: "You can install the package on UPM.",
|
||||||
|
actionLabel: "Migrate To UPM",
|
||||||
|
onClick: GetOwner().MigrateToUpm,
|
||||||
|
style: GroupBackgroundStyle);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Event handler methods
|
||||||
|
|
||||||
|
protected virtual void OnSectionStatusChange(EditorSectionInfo section)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
protected virtual void OnFocusSectionValueChange(EditorSectionInfo section)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Nested types
|
||||||
|
|
||||||
|
protected class ButtonMeta
|
||||||
|
{
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
public string Label { get; private set; }
|
||||||
|
|
||||||
|
public System.Action OnClick { get; private set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public ButtonMeta(string label, System.Action onClick)
|
||||||
|
{
|
||||||
|
// set properties
|
||||||
|
Label = label;
|
||||||
|
OnClick = onClick;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class DefaultTabs
|
||||||
|
{
|
||||||
|
public const string kGeneral = "General";
|
||||||
|
|
||||||
|
public const string kServices = "Services";
|
||||||
|
|
||||||
|
public const string kMisc = "Help";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b40748a9ae9854787b64a0c8ccc993a9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,89 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
#if UNITY_2019_1_OR_NEWER
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
#else
|
||||||
|
using UnityEngine.Experimental.UIElements;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public class SettingsProviderZ : SettingsProvider
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
private SettingsObject m_settingsObject;
|
||||||
|
|
||||||
|
private SettingsObjectInspector m_settingsObjectInspector;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
private SettingsProviderZ(SettingsObject settingsObject, string path, SettingsScope scopes)
|
||||||
|
: base(path, scopes)
|
||||||
|
{
|
||||||
|
// set properties
|
||||||
|
keywords = GetSearchKeywordsFromSerializedObject(new SerializedObject(settingsObject));
|
||||||
|
m_settingsObject = settingsObject;
|
||||||
|
m_settingsObjectInspector = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Create methods
|
||||||
|
|
||||||
|
public static SettingsProviderZ Create(SettingsObject settingsObject, string path, SettingsScope scopes)
|
||||||
|
{
|
||||||
|
return new SettingsProviderZ(settingsObject, path, scopes);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Base class methods
|
||||||
|
|
||||||
|
public override void OnActivate(string searchContext, VisualElement rootElement)
|
||||||
|
{
|
||||||
|
base.OnActivate(searchContext, rootElement);
|
||||||
|
|
||||||
|
m_settingsObjectInspector = UnityEditor.Editor.CreateEditor(m_settingsObject) as SettingsObjectInspector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnTitleBarGUI()
|
||||||
|
{
|
||||||
|
EditorGUILayout.InspectorTitlebar(false, m_settingsObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnGUI(string searchContext)
|
||||||
|
{
|
||||||
|
if (m_settingsObjectInspector == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
GUILayout.Space(10f);
|
||||||
|
EditorGUILayout.BeginVertical();
|
||||||
|
m_settingsObjectInspector.OnInspectorGUI();
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
GUILayout.Space(10f);
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnDeactivate()
|
||||||
|
{
|
||||||
|
base.OnDeactivate();
|
||||||
|
|
||||||
|
if (m_settingsObjectInspector)
|
||||||
|
{
|
||||||
|
Object.DestroyImmediate(m_settingsObjectInspector);
|
||||||
|
m_settingsObjectInspector = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 82c92451806c941888c94130f02c1a18
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using VoxelBusters.CoreLibrary;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
[CustomPropertyDrawer(typeof(StringPopupAttribute), true)]
|
||||||
|
public class StringPopupAttributeDrawer : PropertyDrawer
|
||||||
|
{
|
||||||
|
#region Base class methods
|
||||||
|
|
||||||
|
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||||
|
{
|
||||||
|
label = EditorGUI.BeginProperty(position, label, property);
|
||||||
|
|
||||||
|
// determine whether popup is required
|
||||||
|
bool canShowPopup = true;
|
||||||
|
var popupAttribute = (StringPopupAttribute)attribute;
|
||||||
|
if (popupAttribute.PreferencePropertyName != null)
|
||||||
|
{
|
||||||
|
var preferencePropertyPath = property.propertyPath.Replace(property.name, popupAttribute.PreferencePropertyName);
|
||||||
|
var preferenceProperty = property.serializedObject.FindProperty(preferencePropertyPath);
|
||||||
|
canShowPopup = (preferenceProperty != null) && (preferenceProperty.boolValue == popupAttribute.PreferencePropertyValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw property as per preference
|
||||||
|
if (canShowPopup)
|
||||||
|
{
|
||||||
|
var options = popupAttribute.Options;
|
||||||
|
int selectedIndex = Array.FindIndex(options, (item) => string.Equals(item, property.stringValue));
|
||||||
|
selectedIndex = EditorGUI.Popup(position, label.text, selectedIndex, options);
|
||||||
|
|
||||||
|
// assign value
|
||||||
|
property.stringValue = (selectedIndex == -1) ? string.Empty : options[selectedIndex];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorGUI.PropertyField(position, property);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUI.EndProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a787cc0f93bc1438c845ec658e1ce1d5
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,105 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public static class UnityEditorUtility
|
||||||
|
{
|
||||||
|
#region Static methods
|
||||||
|
|
||||||
|
public static bool ShowFoldableHeader(string prefKeyName, string content, string tooltip = null)
|
||||||
|
{
|
||||||
|
bool isExpanded = EditorGUILayout.Foldout(EditorPrefs.GetBool(prefKeyName, false), new GUIContent(content, tooltip));
|
||||||
|
EditorPrefs.SetBool(prefKeyName, isExpanded);
|
||||||
|
|
||||||
|
return isExpanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ShowFoldableHeader(SerializedProperty property, string displayName = null)
|
||||||
|
{
|
||||||
|
bool isExpanded = EditorGUILayout.Foldout(property.isExpanded, new GUIContent(displayName ?? property.displayName, property.tooltip));
|
||||||
|
property.isExpanded = isExpanded;
|
||||||
|
|
||||||
|
return isExpanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetIsEditorDirty(bool value)
|
||||||
|
{
|
||||||
|
EditorPrefs.SetBool("editor-is-dirty", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool GetIsEditorDirty()
|
||||||
|
{
|
||||||
|
return EditorPrefs.GetBool("editor-is-dirty", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Mask field methods
|
||||||
|
|
||||||
|
public static void EnumFlagsField(Rect position, GUIContent label, SerializedProperty property, Type type)
|
||||||
|
{
|
||||||
|
property.intValue = EnumFlagsField(position, label, property.intValue, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int EnumFlagsField(Rect position, GUIContent label, int value, Type type)
|
||||||
|
{
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
|
#if UNITY_2017_3_OR_NEWER
|
||||||
|
Enum newValue = EditorGUI.EnumFlagsField(position, label, GetValueAsEnum(value, type));
|
||||||
|
#else
|
||||||
|
Enum newValue = EditorGUI.EnumMaskField(position, label, GetValueAsEnum(value, type));
|
||||||
|
#endif
|
||||||
|
if (EditorGUI.EndChangeCheck())
|
||||||
|
{
|
||||||
|
return GetEnumAsInt(newValue, type);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T EnumFlagsField<T>(Rect position, string label, T value)
|
||||||
|
{
|
||||||
|
return (T)(object)EnumFlagsField(position, new GUIContent(label), (int)(object)value, typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private static methods
|
||||||
|
|
||||||
|
private static Array GetEnumValues(Type type)
|
||||||
|
{
|
||||||
|
return Enum.GetValues(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Enum GetValueAsEnum(int value, Type type)
|
||||||
|
{
|
||||||
|
return (Enum)Enum.ToObject(type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetEnumAsInt(Enum value, Type type)
|
||||||
|
{
|
||||||
|
int newValueInt = Convert.ToInt32(value);
|
||||||
|
|
||||||
|
// if "Everything" is set, force Unity to unset the extra bits by iterating through them
|
||||||
|
if (newValueInt < 0)
|
||||||
|
{
|
||||||
|
int bits = 0;
|
||||||
|
foreach (var enumValue in GetEnumValues(type))
|
||||||
|
{
|
||||||
|
int checkBit = newValueInt & (int)enumValue;
|
||||||
|
if (checkBit != 0)
|
||||||
|
{
|
||||||
|
bits |= (int)enumValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newValueInt = bits;
|
||||||
|
}
|
||||||
|
return newValueInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8a74bb08ae57c4ca79d5d452cffa4c63
|
||||||
|
timeCreated: 1576323712
|
||||||
|
licenseType: Store
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f1aed5f1786094c25bd1a7e0d9ebaffb
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,115 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.PackageManager;
|
||||||
|
using UnityEditor.PackageManager.Requests;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public class AddUnityPackageOperation
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
private string m_package;
|
||||||
|
|
||||||
|
private Action m_callback;
|
||||||
|
|
||||||
|
private AddRequest m_addPackageRequest;
|
||||||
|
|
||||||
|
private ListRequest m_getPackagesRequest;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public AddUnityPackageOperation(string package, System.Action callback)
|
||||||
|
{
|
||||||
|
// set properties
|
||||||
|
m_package = package;
|
||||||
|
m_callback = callback;
|
||||||
|
m_getPackagesRequest = null;
|
||||||
|
m_addPackageRequest = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public methods
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
m_getPackagesRequest = Client.List();
|
||||||
|
|
||||||
|
// register for routine callbacks
|
||||||
|
EditorApplication.update += EditorUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private methods
|
||||||
|
|
||||||
|
private void EditorUpdate()
|
||||||
|
{
|
||||||
|
// check whether dependency packages are already installed
|
||||||
|
if (m_getPackagesRequest != null)
|
||||||
|
{
|
||||||
|
if (m_getPackagesRequest.IsCompleted)
|
||||||
|
{
|
||||||
|
bool packageInstalled = false;
|
||||||
|
foreach (var item in m_getPackagesRequest.Result)
|
||||||
|
{
|
||||||
|
if (string.Equals(item.name, m_package))
|
||||||
|
{
|
||||||
|
packageInstalled = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset state
|
||||||
|
m_getPackagesRequest = null;
|
||||||
|
|
||||||
|
// create add request, incase if package is not installed
|
||||||
|
if (!packageInstalled)
|
||||||
|
{
|
||||||
|
Debug.LogFormat("[VoxelBusters] Creating request to add package {0}", m_package);
|
||||||
|
m_addPackageRequest = Client.Add(m_package);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SendCompletionCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// import resources after required packages are installed
|
||||||
|
if (m_addPackageRequest != null)
|
||||||
|
{
|
||||||
|
if (m_addPackageRequest.IsCompleted)
|
||||||
|
{
|
||||||
|
SendCompletionCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendCompletionCallback()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_callback();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// reset state
|
||||||
|
m_package = null;
|
||||||
|
m_callback = null;
|
||||||
|
m_addPackageRequest = null;
|
||||||
|
m_getPackagesRequest = null;
|
||||||
|
EditorApplication.update -= EditorUpdate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7a35410a8a14c41b3ae3d22ba17a44fe
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,152 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.Build;
|
||||||
|
using UnityEngine;
|
||||||
|
using VoxelBusters.CoreLibrary;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public static class ScriptingDefinesManager
|
||||||
|
{
|
||||||
|
#region Static fields
|
||||||
|
|
||||||
|
private static Dictionary<BuildTargetGroup, List<string>> s_addDefinesCollection;
|
||||||
|
|
||||||
|
private static Dictionary<BuildTargetGroup, List<string>> s_removeDefinesCollection;
|
||||||
|
|
||||||
|
private static BuildTargetGroup[] s_supportedTargetGroups;
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Static methods
|
||||||
|
|
||||||
|
public static void AddDefine(string define, params BuildTargetGroup[] targetGroups)
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
|
||||||
|
AddDefineToCollection(
|
||||||
|
definesCollection: s_addDefinesCollection,
|
||||||
|
define: define,
|
||||||
|
targetGroups: GetBuildTargetGroupsOrDefault(targetGroups));
|
||||||
|
UpdateDefineSymbolsDelayed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RemoveDefine(string define, params BuildTargetGroup[] targetGroups)
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
|
||||||
|
AddDefineToCollection(
|
||||||
|
definesCollection: s_removeDefinesCollection,
|
||||||
|
define: define,
|
||||||
|
targetGroups: GetBuildTargetGroupsOrDefault(targetGroups));
|
||||||
|
UpdateDefineSymbolsDelayed();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureInitialized()
|
||||||
|
{
|
||||||
|
if (s_addDefinesCollection != null) return;
|
||||||
|
|
||||||
|
// set properties
|
||||||
|
s_addDefinesCollection = new Dictionary<BuildTargetGroup, List<string>>();
|
||||||
|
s_removeDefinesCollection = new Dictionary<BuildTargetGroup, List<string>>();
|
||||||
|
s_supportedTargetGroups = GetSupportedBuildTargetGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BuildTargetGroup[] GetSupportedBuildTargetGroups()
|
||||||
|
{
|
||||||
|
var newList = new List<BuildTargetGroup>();
|
||||||
|
foreach (BuildTarget buildTarget in System.Enum.GetValues(typeof(BuildTarget)))
|
||||||
|
{
|
||||||
|
var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(buildTarget);
|
||||||
|
if (BuildPipeline.IsBuildTargetSupported(buildTargetGroup, buildTarget))
|
||||||
|
{
|
||||||
|
newList.AddUnique(buildTargetGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newList.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BuildTargetGroup[] GetBuildTargetGroupsOrDefault(BuildTargetGroup[] targetGroups)
|
||||||
|
{
|
||||||
|
return ((targetGroups == null) || (targetGroups.Length == 0))
|
||||||
|
? s_supportedTargetGroups
|
||||||
|
: targetGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddDefineToCollection(Dictionary<BuildTargetGroup, List<string>> definesCollection, string define, BuildTargetGroup[] targetGroups)
|
||||||
|
{
|
||||||
|
// add define symbol for all the specified target groups
|
||||||
|
foreach (var group in targetGroups)
|
||||||
|
{
|
||||||
|
if (!System.Array.Exists(s_supportedTargetGroups, (item) => (item == group)))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!definesCollection.TryGetValue(group, out List<string> groupDefines))
|
||||||
|
{
|
||||||
|
var newDefines = new List<string>();
|
||||||
|
definesCollection.Add(group, newDefines);
|
||||||
|
|
||||||
|
groupDefines = newDefines;
|
||||||
|
}
|
||||||
|
|
||||||
|
groupDefines.AddUnique(define);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateDefineSymbolsDelayed()
|
||||||
|
{
|
||||||
|
EditorApplication.delayCall -= UpdateDefineSymbols;
|
||||||
|
EditorApplication.delayCall += UpdateDefineSymbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateDefineSymbols()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
AssetDatabase.StartAssetEditing();
|
||||||
|
|
||||||
|
foreach (var targetGroup in s_supportedTargetGroups)
|
||||||
|
{
|
||||||
|
var existingDefines = PlayerSettings.GetScriptingDefineSymbols(NamedBuildTarget.FromBuildTargetGroup(targetGroup)).Split(';');
|
||||||
|
var updatedDefines = new List<string>(existingDefines);
|
||||||
|
bool isModified = false;
|
||||||
|
if (s_addDefinesCollection.TryGetValue(targetGroup, out List<string> addDefines))
|
||||||
|
{
|
||||||
|
foreach (var define in addDefines)
|
||||||
|
{
|
||||||
|
isModified |= updatedDefines.AddUnique(define);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (s_removeDefinesCollection.TryGetValue(targetGroup, out List<string> removeDefines))
|
||||||
|
{
|
||||||
|
foreach (var define in removeDefines)
|
||||||
|
{
|
||||||
|
isModified |= updatedDefines.Remove(define);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set values if there are modifications
|
||||||
|
if (isModified)
|
||||||
|
{
|
||||||
|
PlayerSettings.SetScriptingDefineSymbols(NamedBuildTarget.FromBuildTargetGroup(targetGroup), string.Join(";", updatedDefines.ToArray()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
AssetDatabase.StopAssetEditing();
|
||||||
|
|
||||||
|
// reset properties
|
||||||
|
s_addDefinesCollection.Clear();
|
||||||
|
s_removeDefinesCollection.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ef477a8c89c614881ab6594601f54f4b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3046d47b6921d4f5e8210ffad4508e17
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,60 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.PackageManager;
|
||||||
|
using UnityEditor.PackageManager.Requests;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public class AddUpmPackageRequest : AsyncOperation<StatusCode>
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
private AddRequest m_request;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
public string Identifier { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public AddUpmPackageRequest(string identifier)
|
||||||
|
{
|
||||||
|
// Set properties
|
||||||
|
Identifier = identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Base class methods
|
||||||
|
|
||||||
|
protected override void OnStart()
|
||||||
|
{
|
||||||
|
m_request = Client.Add(Identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnUpdate()
|
||||||
|
{
|
||||||
|
if (!m_request.IsCompleted) return;
|
||||||
|
|
||||||
|
// Process response
|
||||||
|
if (m_request.Status == StatusCode.Success)
|
||||||
|
{
|
||||||
|
Debug.Log($"Installed package: {m_request.Result.packageId} successfully.");
|
||||||
|
SetIsCompleted(StatusCode.Success);
|
||||||
|
}
|
||||||
|
else if (m_request.Status == StatusCode.Failure)
|
||||||
|
{
|
||||||
|
Debug.Log($"Failed to install package: {m_request.Result.packageId}. Error {m_request.Error.message}.");
|
||||||
|
SetIsCompleted(error: new Error(m_request.Error.message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a87b7bce048e1424984e245b62107603
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,61 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
|
||||||
|
public partial class AssemblyDefinitionProxy
|
||||||
|
{
|
||||||
|
private struct AssemblyDefinitionData
|
||||||
|
{
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
[JsonProperty("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("references")]
|
||||||
|
public string[] References { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("optionalUnityReferences")]
|
||||||
|
public string[] OptionalUnityReferences { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("includePlatforms")]
|
||||||
|
public string[] IncludePlatforms { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("excludePlatforms")]
|
||||||
|
public string[] ExcludePlatforms { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("allowUnsafeCode")]
|
||||||
|
public bool AllowUnsafeCode { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("overrideReferences")]
|
||||||
|
public bool OverrideReferences { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("precompiledReferences")]
|
||||||
|
public string[] PrecompiledReferences { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("autoReferenced")]
|
||||||
|
public bool AutoReferenced { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("defineConstraints")]
|
||||||
|
public string[] DefineConstraints { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public methods
|
||||||
|
|
||||||
|
public static AssemblyDefinitionData Load(string dataString)
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject<AssemblyDefinitionData>(dataString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ToJson()
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(this, Formatting.Indented);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6f335c720b36b4411b4d54550b2e94d8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,52 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.Compilation;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
|
||||||
|
public partial class AssemblyDefinitionProxy
|
||||||
|
{
|
||||||
|
|
||||||
|
private AssemblyDefinitionData m_data;
|
||||||
|
private string m_directoryPath;
|
||||||
|
|
||||||
|
public AssemblyDefinitionProxy(string assemblyDirectoryPath)
|
||||||
|
{
|
||||||
|
string asmdefFile = Directory.GetFiles(assemblyDirectoryPath, "*.asmdef").FirstOrDefault();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(asmdefFile))
|
||||||
|
{
|
||||||
|
throw new VBException($"No .asmdef file found in {assemblyDirectoryPath} directory.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_directoryPath = assemblyDirectoryPath;
|
||||||
|
string contents = IOServices.ReadFile(asmdefFile);
|
||||||
|
m_data = AssemblyDefinitionData.Load(contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void IncludeAllPlatforms()
|
||||||
|
{
|
||||||
|
m_data.ExcludePlatforms = new string[0];
|
||||||
|
m_data.IncludePlatforms = new string[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExcludeAllPlatforms()
|
||||||
|
{
|
||||||
|
AssemblyDefinitionPlatform[] platforms = CompilationPipeline.GetAssemblyDefinitionPlatforms();
|
||||||
|
m_data.ExcludePlatforms = platforms.Select(platform => platform.Name).ToArray();
|
||||||
|
m_data.IncludePlatforms = new string[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
IOServices.CreateFile(IOServices.CombinePath(m_directoryPath, $"{m_data.Name}.asmdef"), m_data.ToJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b5c04f18545af4d43bfe1034aa18a54e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,39 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public static class AssemblyDefinitionServices
|
||||||
|
{
|
||||||
|
#region Static methods
|
||||||
|
|
||||||
|
public static void CreateDefinition(string path,
|
||||||
|
string name,
|
||||||
|
string[] includePlatforms = null,
|
||||||
|
string[] references = null)
|
||||||
|
{
|
||||||
|
string json = $"{{" +
|
||||||
|
$"\n\t\"name\":\"{name}\"," +
|
||||||
|
$"\n\t\"includePlatforms\":{Jsonify(includePlatforms)}," +
|
||||||
|
$"\n\t\"references\":{Jsonify(references)}" +
|
||||||
|
$"\n}}";
|
||||||
|
|
||||||
|
if (!IOServices.DirectoryExists(path))
|
||||||
|
{
|
||||||
|
IOServices.CreateDirectory(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
IOServices.CreateFile(IOServices.CombinePath(path, $"{name}.asmdef"), json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Jsonify(string[] array)
|
||||||
|
{
|
||||||
|
if (array == null) return "[]";
|
||||||
|
|
||||||
|
return $"[{string.Join(",", System.Array.ConvertAll(array, (item) => $"\"{item}\""))}]";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3f85adcf2acaf49838d6c77837b24ca1
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,90 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public static class AssetDatabaseUtility
|
||||||
|
{
|
||||||
|
#region Resources methods
|
||||||
|
|
||||||
|
public static void CreateFolder(string folder)
|
||||||
|
{
|
||||||
|
var pathComponents = folder.Split('/');
|
||||||
|
|
||||||
|
string currentPath = string.Empty;
|
||||||
|
for (int iter = 0; iter < pathComponents.Length; iter++)
|
||||||
|
{
|
||||||
|
string component = pathComponents[iter];
|
||||||
|
string newPath = Path.Combine(currentPath, component);
|
||||||
|
if (!AssetDatabase.IsValidFolder(newPath))
|
||||||
|
{
|
||||||
|
AssetDatabase.CreateFolder(currentPath, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update path
|
||||||
|
currentPath = newPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CreateAssetAtPath(Object asset,
|
||||||
|
string assetPath)
|
||||||
|
{
|
||||||
|
// create container folder
|
||||||
|
string parentFolder = assetPath.Substring(0, assetPath.LastIndexOf('/'));
|
||||||
|
CreateFolder(parentFolder);
|
||||||
|
|
||||||
|
// create asset
|
||||||
|
AssetDatabase.CreateAsset(asset, assetPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T CreateScriptableObject<T>(string assetPath,
|
||||||
|
System.Func<T> createFunc = null,
|
||||||
|
System.Action<T> onInit = null) where T : ScriptableObject
|
||||||
|
{
|
||||||
|
var instance = (createFunc != null)
|
||||||
|
? createFunc()
|
||||||
|
: ScriptableObject.CreateInstance<T>();
|
||||||
|
onInit?.Invoke(instance);
|
||||||
|
|
||||||
|
// create file
|
||||||
|
CreateAssetAtPath(instance, assetPath);
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T LoadScriptableObject<T>(string assetPath,
|
||||||
|
System.Action<T> onLoad = null,
|
||||||
|
System.Func<System.Exception> throwErrorFunc = null) where T : ScriptableObject
|
||||||
|
{
|
||||||
|
var instance = AssetDatabase.LoadAssetAtPath<T>(assetPath);
|
||||||
|
if (instance)
|
||||||
|
{
|
||||||
|
onLoad?.Invoke(instance);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (throwErrorFunc != null)
|
||||||
|
{
|
||||||
|
throw throwErrorFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T[] FindAssetObjects<T>() where T : ScriptableObject
|
||||||
|
{
|
||||||
|
var guids = AssetDatabase.FindAssets($"t:{typeof(T).Name}");
|
||||||
|
return System.Array.ConvertAll(guids, (item) =>
|
||||||
|
{
|
||||||
|
string path = AssetDatabase.GUIDToAssetPath(item);
|
||||||
|
return AssetDatabase.LoadAssetAtPath<T>(path);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ab0d5c5a093844a80ae39b5d66ab9164
|
||||||
|
timeCreated: 1576669974
|
||||||
|
licenseType: Store
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,39 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using System.Collections;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public class DeleteAssetRequest : AsyncOperation<bool>
|
||||||
|
{
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
public string Path { get; private set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public DeleteAssetRequest(string path)
|
||||||
|
{
|
||||||
|
Path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Base class methods
|
||||||
|
|
||||||
|
protected override void OnStart()
|
||||||
|
{
|
||||||
|
if (!AssetDatabase.DeleteAsset(Path))
|
||||||
|
{
|
||||||
|
SetIsCompleted(new Error("File not found."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetIsCompleted(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2204697bc932f4f28b4dad14e5c29724
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,31 @@
|
|||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using VoxelBusters.CoreLibrary;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public static class EditorApplicationUtility
|
||||||
|
{
|
||||||
|
#region Platform methods
|
||||||
|
|
||||||
|
public static RuntimePlatform ConvertBuildTargetToRuntimePlatform(BuildTarget buildTarget)
|
||||||
|
{
|
||||||
|
switch (buildTarget)
|
||||||
|
{
|
||||||
|
case BuildTarget.iOS:
|
||||||
|
return RuntimePlatform.IPhonePlayer;
|
||||||
|
|
||||||
|
case BuildTarget.tvOS:
|
||||||
|
return RuntimePlatform.tvOS;
|
||||||
|
|
||||||
|
case BuildTarget.Android:
|
||||||
|
return RuntimePlatform.Android;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (RuntimePlatform)(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4f0eb0657c7f942c6a847903ed852752
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,40 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using System.Collections;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public class ImportPackageRequest : VoxelBusters.CoreLibrary.AsyncOperation<bool>
|
||||||
|
{
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
public string Path { get; private set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public ImportPackageRequest(string path)
|
||||||
|
{
|
||||||
|
Path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Base class methods
|
||||||
|
|
||||||
|
protected override void OnStart()
|
||||||
|
{
|
||||||
|
if (!IOServices.FileExists(Path))
|
||||||
|
{
|
||||||
|
SetIsCompleted(new Error("File not found."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetDatabase.ImportPackage(Path, interactive: false);
|
||||||
|
SetIsCompleted(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a33d068e4b51149e08021407780741d2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,60 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.PackageManager;
|
||||||
|
using UnityEditor.PackageManager.Requests;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public class RemoveUpmPackageRequest : AsyncOperation<StatusCode>
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
|
||||||
|
private RemoveRequest m_request;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
public string Identifier { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public RemoveUpmPackageRequest(string identifier)
|
||||||
|
{
|
||||||
|
// Set properties
|
||||||
|
Identifier = identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Base class methods
|
||||||
|
|
||||||
|
protected override void OnStart()
|
||||||
|
{
|
||||||
|
m_request = Client.Remove(Identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnUpdate()
|
||||||
|
{
|
||||||
|
if (!m_request.IsCompleted) return;
|
||||||
|
|
||||||
|
// Process response
|
||||||
|
if (m_request.Status == StatusCode.Success)
|
||||||
|
{
|
||||||
|
Debug.Log($"Installed package: {m_request.PackageIdOrName} successfully.");
|
||||||
|
SetIsCompleted(m_request.Status);
|
||||||
|
}
|
||||||
|
else if (m_request.Status >= StatusCode.Failure)
|
||||||
|
{
|
||||||
|
Debug.Log($"Failed to install package: {m_request.PackageIdOrName}. Error {m_request.Error.message}.");
|
||||||
|
SetIsCompleted(error: new Error(m_request.Error.message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b5ea45e566aa3452aa8db2aa084333c4
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,122 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public class TypeCache
|
||||||
|
{
|
||||||
|
#region Static fields
|
||||||
|
|
||||||
|
private static Dictionary<Type, string> s_typeMap;
|
||||||
|
|
||||||
|
private static bool s_isDirty;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Static methods
|
||||||
|
|
||||||
|
public static void Rebuild()
|
||||||
|
{
|
||||||
|
// Reset properties
|
||||||
|
s_isDirty = true;
|
||||||
|
s_typeMap?.Clear();
|
||||||
|
|
||||||
|
EnsureCacheIsUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<MemberInfo, TAttribute> GetMembersWithAttribute<TAttribute>(MemberTypes memberTypes, BindingFlags bindingAttr)
|
||||||
|
where TAttribute : Attribute
|
||||||
|
{
|
||||||
|
return GetMembersWithAttribute<MemberInfo, TAttribute>(memberTypes, bindingAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<FieldInfo, TAttribute> GetFieldsWithAttribute<TAttribute>(BindingFlags bindingAttr) where TAttribute : Attribute
|
||||||
|
{
|
||||||
|
return GetMembersWithAttribute<FieldInfo, TAttribute>(MemberTypes.Field, bindingAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<PropertyInfo, TAttribute> GetPropertiesWithAttribute<TAttribute>(BindingFlags bindingAttr) where TAttribute : Attribute
|
||||||
|
{
|
||||||
|
return GetMembersWithAttribute<PropertyInfo, TAttribute>(MemberTypes.Property, bindingAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<EventInfo, TAttribute> GetEventsWithAttribute<TAttribute>(BindingFlags bindingAttr) where TAttribute : Attribute
|
||||||
|
{
|
||||||
|
return GetMembersWithAttribute<EventInfo, TAttribute>(MemberTypes.Event, bindingAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<MethodInfo, TAttribute> GetMethodsWithAttribute<TAttribute>(BindingFlags bindingAttr) where TAttribute : Attribute
|
||||||
|
{
|
||||||
|
return GetMembersWithAttribute<MethodInfo, TAttribute>(MemberTypes.Method, bindingAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private methods
|
||||||
|
|
||||||
|
private static void EnsureCacheIsUpdated()
|
||||||
|
{
|
||||||
|
if (!s_isDirty) return;
|
||||||
|
|
||||||
|
// Initialize object
|
||||||
|
s_isDirty = false;
|
||||||
|
if (s_typeMap == null)
|
||||||
|
{
|
||||||
|
s_typeMap = new Dictionary<Type, string>(capacity: 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add types to the cache
|
||||||
|
foreach (var type in ReflectionUtility.FindAllTypes())
|
||||||
|
{
|
||||||
|
s_typeMap.Add(type, type.FullName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<TMemberInfo, TAttribute> GetMembersWithAttribute<TMemberInfo, TAttribute>(MemberTypes memberTypes, BindingFlags bindingAttr)
|
||||||
|
where TMemberInfo : MemberInfo
|
||||||
|
where TAttribute : Attribute
|
||||||
|
{
|
||||||
|
EnsureCacheIsUpdated();
|
||||||
|
|
||||||
|
// Find all the methods with specified attribute
|
||||||
|
var attributeType = typeof(TAttribute);
|
||||||
|
var collection = new Dictionary<TMemberInfo, TAttribute>();
|
||||||
|
foreach (var mapItem in s_typeMap)
|
||||||
|
{
|
||||||
|
var currentType = mapItem.Key;
|
||||||
|
|
||||||
|
AddMembersWithRequiredAttributes(currentType);
|
||||||
|
|
||||||
|
//When we create a "concrete" class derived from a generic class, it will internally constructs a new class (replacing generic parameters with actual types). We need to query this type for required attributes as well.
|
||||||
|
if (IsConstructedClosedGenericType(currentType.BaseType))
|
||||||
|
{
|
||||||
|
AddMembersWithRequiredAttributes(currentType.BaseType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return collection;
|
||||||
|
|
||||||
|
void AddMembersWithRequiredAttributes(Type type)
|
||||||
|
{
|
||||||
|
var members = type.FindMembers(memberTypes, bindingAttr, null, null);
|
||||||
|
foreach (var memberInfo in members)
|
||||||
|
{
|
||||||
|
var attributes = memberInfo.GetCustomAttributes(attributeType, false);
|
||||||
|
if (attributes.IsNullOrEmpty()) continue;
|
||||||
|
|
||||||
|
collection.Add(memberInfo as TMemberInfo, attributes[0] as TAttribute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsConstructedClosedGenericType(Type type)
|
||||||
|
{
|
||||||
|
return type != null && type.IsConstructedGenericType && !type.ContainsGenericParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8d14cdf5aeeb847f0ac54ffd5c03ae1d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,89 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor
|
||||||
|
{
|
||||||
|
public static class UnityPackageServices
|
||||||
|
{
|
||||||
|
#region Constants
|
||||||
|
|
||||||
|
private static readonly string[] s_staticFolders =
|
||||||
|
{
|
||||||
|
"Essentials",
|
||||||
|
"Examples",
|
||||||
|
"Extras",
|
||||||
|
"Plugins",
|
||||||
|
"Scripts"
|
||||||
|
};
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Static methods
|
||||||
|
|
||||||
|
[System.Obsolete("This method is deprecated. Use MigrateToUpm instead.")]
|
||||||
|
public static void MigrateToUPM(this UnityPackageDefinition package)
|
||||||
|
{
|
||||||
|
MovePackageToUpmRecursively(package, refreshOnFinish: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void MigrateToUpm(this UnityPackageDefinition package)
|
||||||
|
{
|
||||||
|
MovePackageToUpmRecursively(package, refreshOnFinish: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void MovePackageToUpmRecursively(this UnityPackageDefinition package, bool refreshOnFinish)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Move dependencies
|
||||||
|
foreach (var dependency in package.Dependencies)
|
||||||
|
{
|
||||||
|
MovePackageToUpmRecursively(dependency, refreshOnFinish: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move main package
|
||||||
|
MovePackageToUpm(package);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (refreshOnFinish)
|
||||||
|
{
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void MovePackageToUpm(UnityPackageDefinition package)
|
||||||
|
{
|
||||||
|
// Confirm that package exists in default install path
|
||||||
|
if (!package.IsInstalledWithinAssets()) return;
|
||||||
|
|
||||||
|
// Move files and folders to new path
|
||||||
|
var sourceDirectory = new DirectoryInfo(package.DefaultInstallPath);
|
||||||
|
IOServices.CreateDirectory(package.UpmInstallPath);
|
||||||
|
foreach (var file in sourceDirectory.GetFiles())
|
||||||
|
{
|
||||||
|
var fileName = file.Name;
|
||||||
|
if (System.Array.Exists(s_staticFolders, (item) => string.Equals(fileName, $"{item}.meta")))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
IOServices.MoveFile(file.FullName, $"{package.UpmInstallPath}/{fileName}");
|
||||||
|
}
|
||||||
|
foreach (var subDirectory in sourceDirectory.GetDirectories())
|
||||||
|
{
|
||||||
|
var subDirectoryName = subDirectory.Name;
|
||||||
|
if (System.Array.Exists(s_staticFolders, (item) => string.Equals(subDirectoryName, item)))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
IOServices.MoveDirectory(subDirectory.FullName, $"{package.UpmInstallPath}/{subDirectoryName}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4667b4f0e71ec4791afd9f9d887a0c96
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "VoxelBusters.CoreLibrary.Editor",
|
||||||
|
"references": [
|
||||||
|
"VoxelBusters.CoreLibrary"
|
||||||
|
],
|
||||||
|
"optionalUnityReferences": [],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": []
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2e305d2a5092a465db2a72838d17a500
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3e4381b029b7441f28679a1978aee2bf
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d402a199f95044159bdc2107cdcd3334
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,25 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor.Experimental
|
||||||
|
{
|
||||||
|
public static class InstallPath
|
||||||
|
{
|
||||||
|
#region Static properties
|
||||||
|
|
||||||
|
public static string EssentialKit { get; private set; } = "https://link.voxelbusters.com/essential-kit";
|
||||||
|
|
||||||
|
public static string ScreenRecorderKit { get; private set; } = "https://link.voxelbusters.com/screen-recorder-kit";
|
||||||
|
|
||||||
|
public static string SocialKit { get; private set; } = "https://link.voxelbusters.com/social-kit";
|
||||||
|
|
||||||
|
public static string MLKit { get; private set; } = "https://link.voxelbusters.com/easy-ml-kit";
|
||||||
|
|
||||||
|
public static string ReportingKit { get; private set; } = "https://link.voxelbusters.com/reporting-kit";
|
||||||
|
|
||||||
|
public static string AdsKit { get; private set; } = "https://link.voxelbusters.com/ads-kit";
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ccdff37e3ee3d41f794ef3d1b107b01d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,65 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace VoxelBusters.CoreLibrary.Editor.Experimental
|
||||||
|
{
|
||||||
|
public static class ProxyMenuManager
|
||||||
|
{
|
||||||
|
#region Constants
|
||||||
|
|
||||||
|
private const string kMenuItemPath = "Window/Voxel Busters";
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Static methods
|
||||||
|
|
||||||
|
#if !ENABLE_VOXELBUSTERS_ESSENTIAL_KIT
|
||||||
|
[MenuItem(kMenuItemPath + "/Essential Kit/Learn More", priority = 0)]
|
||||||
|
public static void InstallEssentialKit()
|
||||||
|
{
|
||||||
|
OpenInstallPath(InstallPath.EssentialKit);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !ENABLE_VOXELBUSTERS_SCREEN_RECORDER_KIT
|
||||||
|
[MenuItem(kMenuItemPath + "/Screen Recorder Kit/Learn More", priority = 0)]
|
||||||
|
public static void InstallScreenRecorderKit()
|
||||||
|
{
|
||||||
|
OpenInstallPath(InstallPath.ScreenRecorderKit);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !ENABLE_VOXELBUSTERS_SOCIAL_KIT
|
||||||
|
[MenuItem(kMenuItemPath + "/Social Kit/Learn More", priority = 0)]
|
||||||
|
public static void InstallSocialKit()
|
||||||
|
{
|
||||||
|
OpenInstallPath(InstallPath.SocialKit);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !ENABLE_VOXELBUSTERS_ML_KIT
|
||||||
|
[MenuItem(kMenuItemPath + "/ML Kit/Learn More", priority = 0)]
|
||||||
|
public static void InstallMLKit()
|
||||||
|
{
|
||||||
|
OpenInstallPath(InstallPath.MLKit);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !ENABLE_VOXELBUSTERS_REPORTING_KIT
|
||||||
|
[MenuItem(kMenuItemPath + "/Reporting Kit/Learn More", priority = 0)]
|
||||||
|
public static void InstallReportingKit()
|
||||||
|
{
|
||||||
|
OpenInstallPath(InstallPath.ReportingKit);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private static void OpenInstallPath(string path)
|
||||||
|
{
|
||||||
|
Application.OpenURL(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8d5313a9f9e1b4bcdb89c991f700da78
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user