添加 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:
SnowShow 2025-03-27 16:12:58 +08:00
parent 4c6ce570c1
commit 3448c86bc2
2624 changed files with 192828 additions and 95 deletions

View File

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

View File

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

View File

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

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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:

File diff suppressed because it is too large Load Diff

View File

@ -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:

View File

@ -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:

View 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.

View File

@ -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:

View File

@ -0,0 +1,897 @@
# External Dependency Manager for Unity
[![openupm](https://img.shields.io/npm/v/com.google.external-dependency-manager?label=openupm&registry_uri=https://package.openupm.com)](https://openupm.com/packages/com.google.external-dependency-manager/)
[![openupm](https://img.shields.io/badge/dynamic/json?color=brightgreen&label=downloads&query=%24.downloads&suffix=%2Fmonth&url=https%3A%2F%2Fpackage.openupm.com%2Fdownloads%2Fpoint%2Flast-month%2Fcom.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
plugins 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`

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -31,7 +31,8 @@
"GUID:dc960734dc080426fa6612f1c5fe95f3",
"GUID:66c2eb417c67ad849907d0769db96dbf",
"GUID:2289059ddf1745b4d80a0f184af99d6b",
"GUID:448b0b55421917e4784a8f2f7449081f"
"GUID:448b0b55421917e4784a8f2f7449081f",
"GUID:bfb3a80268dac420ab25cd26e09e4475"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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();
}
}
}
}
}

View File

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

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 71ff8e366aef840198f173c1b05160fc
timeCreated: 1579541126
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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,
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 11e33d767c33a4a10bc1e378ed17ef72
timeCreated: 1579074419
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f924f934566204936b517248f5f8f4bd
timeCreated: 1576323713
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cafbc9653f53348559d4e088e2014b44
timeCreated: 1576323712
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9c5bbe7cdfcd444908de802817480fbf
timeCreated: 1577514657
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8a74bb08ae57c4ca79d5d452cffa4c63
timeCreated: 1576323712
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}
}

View File

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

View File

@ -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());
}
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ab0d5c5a093844a80ae39b5d66ab9164
timeCreated: 1576669974
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

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

View File

@ -0,0 +1,16 @@
{
"name": "VoxelBusters.CoreLibrary.Editor",
"references": [
"VoxelBusters.CoreLibrary"
],
"optionalUnityReferences": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": []
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2e305d2a5092a465db2a72838d17a500
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

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

View File

@ -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
}
}

View File

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

View File

@ -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
}
}

View File

@ -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