├── .editorconfig ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md └── workflows │ └── release.yml ├── .gitignore ├── .releaserc ├── CHANGELOG.md ├── CHANGELOG.md.meta ├── Editor.meta ├── Editor ├── Attributes.meta ├── Attributes │ ├── ClassesOnlyAttribute.cs │ └── ClassesOnlyAttribute.cs.meta ├── Builders.meta ├── Builders │ ├── AssetsItemBuilder.cs │ ├── AssetsItemBuilder.cs.meta │ ├── ClassesItemBuilder.cs │ ├── ClassesItemBuilder.cs.meta │ ├── SceneItemBuilder.cs │ └── SceneItemBuilder.cs.meta ├── Drawers.meta ├── Drawers │ ├── CustomObjectDrawer.Styles.cs │ ├── CustomObjectDrawer.Styles.cs.meta │ ├── CustomObjectDrawer.cs │ ├── CustomObjectDrawer.cs.meta │ ├── IReferenceDrawer.cs │ ├── IReferenceDrawer.cs.meta │ ├── RawReferenceDrawer.cs │ ├── RawReferenceDrawer.cs.meta │ ├── ReferenceDrawer.cs │ ├── ReferenceDrawer.cs.meta │ ├── UnityReferenceDrawer.cs │ └── UnityReferenceDrawer.cs.meta ├── Items.meta ├── Items │ ├── AssetDropdownItem.cs │ ├── AssetDropdownItem.cs.meta │ ├── ClassDropdownItem.cs │ ├── ClassDropdownItem.cs.meta │ ├── IDropdownItem.cs │ ├── IDropdownItem.cs.meta │ ├── NoneDropdownItem.cs │ ├── NoneDropdownItem.cs.meta │ ├── SceneDropdownItem.cs │ └── SceneDropdownItem.cs.meta ├── SerializableInterfacePropertyDrawer.cs ├── SerializableInterfacePropertyDrawer.cs.meta ├── TNRD.SerializableInterface.Editor.asmdef ├── TNRD.SerializableInterface.Editor.asmdef.meta ├── Utilities.meta └── Utilities │ ├── AdvancedDropdownItemWrapper.cs │ ├── AdvancedDropdownItemWrapper.cs.meta │ ├── IconUtility.cs │ ├── IconUtility.cs.meta │ ├── PropertyEditorUtility.cs │ ├── PropertyEditorUtility.cs.meta │ ├── ReflectedEditorGUI.cs │ ├── ReflectedEditorGUI.cs.meta │ ├── SerializableInterfaceAdvancedDropdown.cs │ ├── SerializableInterfaceAdvancedDropdown.cs.meta │ ├── SerializedPropertyExtensions.cs │ ├── SerializedPropertyExtensions.cs.meta │ ├── SerializedPropertyUtilities.cs │ └── SerializedPropertyUtilities.cs.meta ├── LICENSE.md ├── LICENSE.md.meta ├── README.md ├── README.md.meta ├── Runtime.meta ├── Runtime ├── AssemblyInfo.cs ├── AssemblyInfo.cs.meta ├── Extensions.cs ├── Extensions.cs.meta ├── ISerializableInterface.cs ├── ISerializableInterface.cs.meta ├── ReferenceMode.cs ├── ReferenceMode.cs.meta ├── SerializableInterface.cs ├── SerializableInterface.cs.meta ├── TNRD.SerializableInterface.asmdef └── TNRD.SerializableInterface.asmdef.meta ├── Samples~ └── SourceGeneration │ ├── SerializableInterfaceSourceGeneration.dll │ └── SerializableInterfaceSourceGeneration.dll.meta ├── package.json └── package.json.meta /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: thundernerd -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Windows] 28 | 29 | **Unity (please complete the following information):** 30 | - Version [e.g. 2021.3.2f1] 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: Thundernerd 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Proposed changes 2 | 3 | Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue. 4 | 5 | ## Types of changes 6 | 7 | _Put an `x` in the boxes that apply._ 8 | 9 | - [ ] Bugfix (non-breaking change which fixes an issue) 10 | - [ ] New feature (non-breaking change which adds functionality) 11 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 12 | - [ ] Refactoring (no functional changes, no api changes) 13 | - [ ] Code style update (formatting, renaming) 14 | - [ ] Build related changes (workflow changes) 15 | - [ ] Documentation Update (readme, changelog) 16 | - [ ] Other, please describe: 17 | 18 | 19 | ## Issue 20 | Include a link to the issue if applicable, otherwise you can remove this section. 21 | 22 | ## What is the current behavior? 23 | Please describe the current behavior that you are modifying. 24 | 25 | ## What is the new behavior? 26 | Please describe the behavior or changes that are being added by this PR. 27 | 28 | ## Checklist 29 | 30 | _Put an `x` in the boxes that apply._ 31 | 32 | - [ ] The code compiles 33 | - [ ] I have tested that this doesn't break existing code (unless it is an explicit breaking change) 34 | - [ ] I have tested that the (new) functionality/fix works as intended 35 | - [ ] I have added necessary documentation (if appropriate) 36 | 37 | ## Further comments 38 | 39 | If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc... 40 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | release: 8 | name: Release 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Setup Node.js 12 | uses: actions/setup-node@v2 13 | with: 14 | node-version: 'lts/*' 15 | - name: Install dependencies 16 | run: > 17 | npm install -g --save false 18 | semantic-release 19 | @semantic-release/changelog 20 | @semantic-release/commit-analyzer 21 | @semantic-release/git 22 | @semantic-release/github 23 | @semantic-release/npm 24 | @semantic-release/release-notes-generator 25 | conventional-changelog-conventionalcommits 26 | - name: Checkout 27 | uses: actions/checkout@v3 28 | with: 29 | token: ${{ secrets.GH_TOKEN }} 30 | - name: Release 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} 33 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 34 | NPM_CONFIG_REGISTRY: 'https://npm.pkg.github.com/@Thundernerd' 35 | run: npx semantic-release -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | package-lock.json.meta 3 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "branches": 3 | [ 4 | "main" 5 | ], 6 | "plugins": 7 | [ 8 | [ 9 | "@semantic-release/commit-analyzer", 10 | { 11 | "preset": "conventionalcommits", 12 | "releaseRules": 13 | [ 14 | { 15 | "breaking": true, 16 | "release": "major" 17 | }, 18 | { 19 | "type": "docs", 20 | "scope": "README", 21 | "release": "patch" 22 | }, 23 | { 24 | "type": "refactor", 25 | "release": "patch" 26 | }, 27 | { 28 | "scope": "no-release", 29 | "release": false 30 | } 31 | ], 32 | "parserOpts": 33 | { 34 | "noteKeywords": 35 | [ 36 | "BREAKING CHANGE", 37 | "BREAKING CHANGES" 38 | ] 39 | } 40 | } 41 | ], 42 | [ 43 | "@semantic-release/npm", 44 | { 45 | "npmPublish": true 46 | } 47 | ], 48 | "@semantic-release/github", 49 | [ 50 | "@semantic-release/release-notes-generator", 51 | { 52 | "preset": "conventionalcommits", 53 | "presetConfig": 54 | { 55 | "types": 56 | [ 57 | { 58 | "type": "feat", 59 | "section": "Features" 60 | }, 61 | { 62 | "type": "fix", 63 | "section": "Bug Fixes" 64 | }, 65 | { 66 | "type": "chore", 67 | "hidden": true 68 | }, 69 | { 70 | "type": "docs", 71 | "section": "Documentation" 72 | }, 73 | { 74 | "type": "style", 75 | "hidden": true 76 | }, 77 | { 78 | "type": "refactor", 79 | "section": "Refactors" 80 | }, 81 | { 82 | "type": "perf", 83 | "section": "Performance Improvements" 84 | }, 85 | { 86 | "type": "test", 87 | "section": "Tests" 88 | } 89 | ] 90 | } 91 | } 92 | ], 93 | [ 94 | "@semantic-release/changelog", 95 | { 96 | "changelogFile": "CHANGELOG.md" 97 | } 98 | ], 99 | [ 100 | "@semantic-release/git", 101 | { 102 | "assets": 103 | [ 104 | "package.json", 105 | "!package-lock.json.meta", 106 | "CHANGELOG.md" 107 | ], 108 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 109 | } 110 | ] 111 | ] 112 | } 113 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [2.2.1](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v2.2.0...v2.2.1) (2024-03-16) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * indentation issues in Unity 2022 ([#63](https://github.com/Thundernerd/Unity3D-SerializableInterface/issues/63)) ([c1648be](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/c1648be54b74dd08ae6076c041e127e897d0b447)) 7 | 8 | ## [2.2.0](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v2.1.0...v2.2.0) (2024-02-09) 9 | 10 | 11 | ### Features 12 | 13 | * instantiation support ([#62](https://github.com/Thundernerd/Unity3D-SerializableInterface/issues/62)) ([de0b647](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/de0b647e2be5cf8809e91e4fc59e81a9ea0c670b)) 14 | 15 | ## [2.1.0](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.12.1...v2.0.0) (2023-11-14) 16 | 17 | 18 | ### Features 19 | 20 | * add classes only attribute ([#57](https://github.com/Thundernerd/Unity3D-SerializableInterface/pull/57)) 21 | 22 | ## [2.0.0](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.12.1...v2.0.0) (2023-11-07) 23 | 24 | 25 | ### ⚠ BREAKING CHANGES 26 | 27 | * add source generation sample 28 | 29 | ### Features 30 | 31 | * add source generation sample ([d5a6da3](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/d5a6da3b59b02e507ee796782765a07ef7d9c004)) 32 | 33 | ### [1.12.1](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.12.0...v1.12.1) (2022-09-10) 34 | 35 | 36 | ### Bug Fixes 37 | 38 | * fix editor drawing of managed classes inside list ([8d66349](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/8d663496eef8eab34e5553617ad9697d3e61d8f1)) 39 | * fix issue with unity version 2021.1 ([d81e9df](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/d81e9df6fbc4fb0eb2ca70c7b3a46418045923aa)), closes [Thundernerd/Unity3D-SerializableInterface#51](https://github.com/Thundernerd/Unity3D-SerializableInterface/issues/51) 40 | * fix version directive in IconUtility ([ffe848c](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/ffe848c940898c22816268d8be2aa3d84721a678)), closes [Thundernerd/Unity3D-SerializableInterface#51](https://github.com/Thundernerd/Unity3D-SerializableInterface/issues/51) 41 | * make changes requested in code review. Also, fix stop condition in for loop that traverses serialized property's path in SerializedPropertyUtilities.GetValue() function ([191d442](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/191d442026fbc37fb86cc980bce68bfb5e8fb874)) 42 | 43 | ## [1.12.0](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.11.1...v1.12.0) (2022-09-09) 44 | 45 | 46 | ### Features 47 | 48 | * added GetValue method for accurately getting a serialized property's value ([96c08c8](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/96c08c88ec3e5f76e34067663ad20b80cd6b6c0f)) 49 | 50 | ### [1.11.1](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.11.0...v1.11.1) (2022-08-02) 51 | 52 | 53 | ### Bug Fixes 54 | 55 | * removed custom line drawing to look more like Unity native ([65b7329](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/65b73293c6f7212db578bb1e1313ea1789abc8d0)) 56 | 57 | ## [1.11.0](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.10.0...v1.11.0) (2022-08-02) 58 | 59 | 60 | ### Features 61 | 62 | * added methods for getting value combined with null checks ([1154dbb](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/1154dbb8f30476daf62fdbe445dfa520615bef5a)) 63 | 64 | ## [1.10.0](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.9.0...v1.10.0) (2022-07-25) 65 | 66 | 67 | ### Features 68 | 69 | * added methods for getting and setting values with serialized properties ([aba0c30](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/aba0c305b87ab843a3842d584a38d58487e61a89)) 70 | * added passing serialized property to CustomObjectDrawer and Dropdown ([5913ab7](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/5913ab76e7759e2264a2979081972468dcae2f40)) 71 | * added serialized property extensions for convenience ([547c892](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/547c89271c892ca1bfa14e30958437c379dc9853)) 72 | 73 | ### [1.9.0](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.8.0...v1.9.0) (2202-07-23) 74 | 75 | 76 | ### What's Changed 77 | * Added RawReferenceDrawer.Styles.cs + DrawLine - #41 by @marc-antoine-girard in https://github.com/Thundernerd/Unity3D-SerializableInterface/pull/42 78 | * ReferenceMode + AvoidDuplicateReferencesInArray by @marc-antoine-girard in https://github.com/Thundernerd/Unity3D-SerializableInterface/pull/43 79 | 80 | ### [1.8.0](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.7.3...v1.8.0) (2022-07-22) 81 | 82 | ### What's Changed 83 | * fix: added null check to prevent null reference exception when clicking properties by @marc-antoine-girard in https://github.com/Thundernerd/Unity3D-SerializableInterface/pull/23 84 | * #24 add none option custom drawers by @marc-antoine-girard in https://github.com/Thundernerd/Unity3D-SerializableInterface/pull/26 85 | * Fixed Drag and Drop behaviour by @marc-antoine-girard in https://github.com/Thundernerd/Unity3D-SerializableInterface/pull/28 86 | * Fixes #25 SerializableInterface maintains instances when set by Inspector by @marc-antoine-girard in https://github.com/Thundernerd/Unity3D-SerializableInterface/pull/27 87 | * #34 add prefabs assets item builder by @marc-antoine-girard in https://github.com/Thundernerd/Unity3D-SerializableInterface/pull/35 88 | 89 | ### [1.7.3](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.7.2...v1.7.3) (2022-07-22) 90 | 91 | 92 | ### Refactors 93 | 94 | * property drawer now reuses cached reference drawers ([f930d8e](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/f930d8ed92b8358e417d075f9a089f7161cadc50)) 95 | 96 | ### [1.7.2](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.7.1...v1.7.2) (2022-07-22) 97 | 98 | 99 | ### Bug Fixes 100 | 101 | * updated actions/checkout to version 3 ([ef748e1](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/ef748e176b864cd3dfc00f3b96e9cedb8783055d)) 102 | 103 | ### [1.7.1](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.7.0...v1.7.1) (2022-06-18) 104 | 105 | 106 | ### Bug Fixes 107 | 108 | * added package-lock.json to gitignore ([9fb467c](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/9fb467c605413529373a9e2a424762744fba1b1c)) 109 | * changed block scalar from literal to folded ([44b269f](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/44b269fa140c1b3a89fba78acb424d8a1609a0b5)) 110 | 111 | ## [1.7.0](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.6.1...v1.7.0) (2022-06-12) 112 | 113 | 114 | ### Features 115 | 116 | * added support for collections ([068b76c](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/068b76cb5072541b4c87a6c5e55669ee69e172be)) 117 | 118 | 119 | ### Bug Fixes 120 | 121 | * invalid scenes get skipped ([b13c42d](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/b13c42d2bcab44bacc71b71a3be3e8f9d40a1e5c)) 122 | * package-lock.json meta issue ([c23b9bc](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/c23b9bc68147acaf165b174a0206ac9cf43b3e6b)) 123 | 124 | ### [1.6.1](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.6.0...v1.6.1) (2022-05-07) 125 | 126 | 127 | ### Bug Fixes 128 | 129 | * add package-lock.json to package ([0b08f32](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/0b08f3251a72b21f61c8df2a4c7ffc0082f5d3d1)) 130 | 131 | ## [1.6.0](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.5.2...v1.6.0) (2022-05-02) 132 | 133 | 134 | ### Features 135 | 136 | * added drag and drop for unity reference ([ef45ebc](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/ef45ebccc6cd6d3a9a6902937ddc1aa3143b27e9)) 137 | * added raw reference drag and drop and combined them in base ([62f61e8](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/62f61e82389bcf97b776ef22cc6c591ef9920ce5)) 138 | 139 | 140 | ### Bug Fixes 141 | 142 | * removed unused namespaces ([0b1185c](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/0b1185c272d7284b26cfcd44295131f5c8a2741a)) 143 | 144 | ### [1.5.2](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.5.1...v1.5.2) (2022-04-28) 145 | 146 | 147 | ### Bug Fixes 148 | 149 | * updated namespaces ([c9acaf2](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/c9acaf2d044f862bf5fdcd0819215d37afc01090)) 150 | 151 | ### [1.5.1](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.5.0...v1.5.1) (2022-04-28) 152 | 153 | 154 | ### Bug Fixes 155 | 156 | * added newline at end of files ([a0d6575](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/a0d6575ef00721d6d3e5b2cb10c2836d5f4a2e03)) 157 | 158 | ## [1.5.0](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.4.1...v1.5.0) (2022-04-28) 159 | 160 | 161 | ### Features 162 | 163 | * added CustomObjectDrawer ([91e0beb](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/91e0beb5fd0e667c2f62cb10c1a8dd6192848040)) 164 | * added IReferenceDrawer ([fe32120](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/fe321208d7de803883b660ae1d0087a2c92569cf)) 165 | * added RawReferenceDrawer and UnityReferenceDrawer ([9e46ddc](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/9e46ddc1de6ba872233798e44cd8353d1451999d)) 166 | 167 | 168 | ### Refactors 169 | 170 | * moved drawing code to separate classes ([81cfffc](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/81cfffc4f3f46a273ba29bebd7beef15273a3782)) 171 | 172 | ### [1.4.1](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.4.0...v1.4.1) (2022-04-28) 173 | 174 | 175 | ### Bug Fixes 176 | 177 | * added missing meta file ([6fd976f](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/6fd976f46d7706f3daacf7b3982f64312f9208c2)) 178 | 179 | ## [1.4.0](https://github.com/Thundernerd/Unity3D-SerializableInterface/compare/v1.3.0...v1.4.0) (2022-04-27) 180 | 181 | 182 | ### Features 183 | 184 | * added semantic release ([74a928c](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/74a928c2818cf04056eaa2a44be386414477ffe5)) 185 | * added semantic-release configuration to package.json ([95e314b](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/95e314bb4c5b784ae10e07a1d2a9a5681421da67)) 186 | * added workflow ([d369a92](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/d369a92f8b04d740cb90622974e6b295c9ff50b8)) 187 | 188 | 189 | ### Bug Fixes 190 | 191 | * moved dependencies to devDependencies ([8441cc8](https://github.com/Thundernerd/Unity3D-SerializableInterface/commit/8441cc8a7c1daf1c00e7c4e379f616b4dace17fa)) 192 | -------------------------------------------------------------------------------- /CHANGELOG.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8a953946a0109894fb71ed8dceeb1dd7 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 00b82c18c6f3d2d4b9b96cf8bf439e71 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Attributes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: beb82a782d0e3f34f9288d36e70fcbbd 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Attributes/ClassesOnlyAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace TNRD 6 | { 7 | /// An attribute to use when you want to exclude non-raw references in the reference picker. 8 | public class ClassesOnlyAttribute : PropertyAttribute { } 9 | } 10 | -------------------------------------------------------------------------------- /Editor/Attributes/ClassesOnlyAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 93d9f96b32dbd774e9f05dc7f675d338 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Builders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b82b77fd561f4d9d93ccfc5b21c046c3 3 | timeCreated: 1650789216 -------------------------------------------------------------------------------- /Editor/Builders/AssetsItemBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using TNRD.Items; 4 | using TNRD.Utilities; 5 | using UnityEditor; 6 | using UnityEditor.IMGUI.Controls; 7 | using UnityEngine; 8 | using UnityEngine.Assertions; 9 | 10 | namespace TNRD.Builders 11 | { 12 | internal sealed class AssetsItemBuilder 13 | { 14 | private readonly HashSet addedItems; 15 | private readonly Dictionary splitToItem; 16 | private readonly Type interfaceType; 17 | 18 | public AssetsItemBuilder(Type interfaceType) 19 | { 20 | Assert.IsNotNull(interfaceType); 21 | Assert.IsTrue(interfaceType.IsInterface); 22 | 23 | addedItems = new HashSet(); 24 | splitToItem = new Dictionary(); 25 | this.interfaceType = interfaceType; 26 | } 27 | 28 | public AdvancedDropdownItem Build() 29 | { 30 | AdvancedDropdownItem root = new AdvancedDropdownItem("Assets"); 31 | splitToItem.Add("Assets/", root); // Needs the trailing slash to be recognized later on 32 | 33 | string[] allAssetPaths = AssetDatabase.GetAllAssetPaths(); 34 | foreach (string assetPath in allAssetPaths) 35 | { 36 | Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath); 37 | if (interfaceType.IsAssignableFrom(assetType)) 38 | { 39 | CreateItemForPath(root, assetPath); 40 | } 41 | else if (assetType == typeof(GameObject)) 42 | { 43 | GameObject gameObject = AssetDatabase.LoadAssetAtPath(assetPath); 44 | if (gameObject.GetComponent(interfaceType) != null) 45 | CreateItemForPath(root, assetPath); 46 | } 47 | } 48 | 49 | return root; 50 | } 51 | 52 | private void CreateItemForPath(AdvancedDropdownItem root, string path) 53 | { 54 | if (addedItems.Contains(path)) 55 | return; 56 | 57 | AdvancedDropdownItem parent = GetOrCreateParentItem(root, path); 58 | parent.AddChild(new AssetDropdownItem(path)); 59 | addedItems.Add(path); 60 | } 61 | 62 | private AdvancedDropdownItem GetOrCreateParentItem(AdvancedDropdownItem root, string path) 63 | { 64 | string currentPath = string.Empty; 65 | string[] splits = path.Split('/'); 66 | 67 | AdvancedDropdownItem item = root; 68 | 69 | for (int i = 0; i < splits.Length - 1; i++) 70 | { 71 | string split = splits[i]; 72 | currentPath += split + '/'; 73 | 74 | if (splitToItem.TryGetValue(currentPath, out AdvancedDropdownItem foundItem)) 75 | { 76 | item = foundItem; 77 | continue; 78 | } 79 | 80 | AdvancedDropdownItem advancedDropdownItem = new AdvancedDropdownItem(split) 81 | { 82 | icon = IconUtility.FolderIcon 83 | }; 84 | item.AddChild(advancedDropdownItem); 85 | item = advancedDropdownItem; 86 | splitToItem.Add(currentPath, advancedDropdownItem); 87 | } 88 | 89 | return item; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Editor/Builders/AssetsItemBuilder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b949b339561e417093a81727d93ef4e1 3 | timeCreated: 1650789274 -------------------------------------------------------------------------------- /Editor/Builders/ClassesItemBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using TNRD.Items; 4 | using TNRD.Utilities; 5 | using UnityEditor; 6 | using UnityEditor.IMGUI.Controls; 7 | using UnityEngine.Assertions; 8 | 9 | namespace TNRD.Builders 10 | { 11 | internal sealed class ClassesItemBuilder 12 | { 13 | private readonly Dictionary splitToItem; 14 | private readonly Type interfaceType; 15 | 16 | public ClassesItemBuilder(Type interfaceType) 17 | { 18 | Assert.IsNotNull(interfaceType); 19 | Assert.IsTrue(interfaceType.IsInterface); 20 | 21 | splitToItem = new Dictionary(); 22 | this.interfaceType = interfaceType; 23 | } 24 | 25 | public AdvancedDropdownItem Build() 26 | { 27 | AdvancedDropdownItem root = new AdvancedDropdownItem("Classes"); 28 | 29 | TypeCache.TypeCollection types = TypeCache.GetTypesDerivedFrom(interfaceType); 30 | foreach (Type type in types) 31 | { 32 | if (type.IsAbstract || type.IsInterface) continue; 33 | if (type.IsSubclassOf(typeof(UnityEngine.Object))) continue; 34 | 35 | AdvancedDropdownItem parent = GetOrCreateParentItem(type, root); 36 | parent.AddChild(new ClassDropdownItem(type)); 37 | } 38 | 39 | return root; 40 | } 41 | 42 | private AdvancedDropdownItem GetOrCreateParentItem(Type type, AdvancedDropdownItem root) 43 | { 44 | Assert.IsNotNull(type); 45 | Assert.IsNotNull(root); 46 | Assert.IsNotNull(splitToItem); 47 | 48 | if (string.IsNullOrEmpty(type.Namespace)) 49 | return root; 50 | 51 | string[] splits = type.Namespace.Split('.'); 52 | 53 | AdvancedDropdownItem splitItem = root; 54 | 55 | string currentPath = string.Empty; 56 | foreach (string split in splits) 57 | { 58 | currentPath += split + '.'; 59 | 60 | if (splitToItem.TryGetValue(currentPath, out AdvancedDropdownItem foundItem)) 61 | { 62 | splitItem = foundItem; 63 | continue; 64 | } 65 | 66 | AdvancedDropdownItem newSplitItem = new AdvancedDropdownItem(split) 67 | { 68 | icon = IconUtility.FolderIcon 69 | }; 70 | splitToItem.Add(currentPath, newSplitItem); 71 | splitItem.AddChild(newSplitItem); 72 | splitItem = newSplitItem; 73 | } 74 | 75 | return splitItem; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Editor/Builders/ClassesItemBuilder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 867f96d4b98f4ca8a4a8f984de333b6a 3 | timeCreated: 1650789182 -------------------------------------------------------------------------------- /Editor/Builders/SceneItemBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using TNRD.Items; 4 | using TNRD.Utilities; 5 | using UnityEditor.IMGUI.Controls; 6 | using UnityEngine; 7 | using UnityEngine.Assertions; 8 | using UnityEngine.SceneManagement; 9 | 10 | namespace TNRD.Builders 11 | { 12 | internal sealed class SceneItemBuilder 13 | { 14 | private readonly Type interfaceType; 15 | private readonly Scene? scene; 16 | 17 | public SceneItemBuilder(Type interfaceType, Scene? scene) 18 | { 19 | Assert.IsNotNull(interfaceType); 20 | 21 | this.interfaceType = interfaceType; 22 | this.scene = scene; 23 | } 24 | 25 | public AdvancedDropdownItem Build() 26 | { 27 | if (scene == null || !scene.Value.IsValid()) 28 | { 29 | return new AdvancedDropdownItem("Scene") 30 | { 31 | enabled = false 32 | }; 33 | } 34 | 35 | AdvancedDropdownItem root = new AdvancedDropdownItemWrapper("Scene"); 36 | 37 | GameObject[] rootGameObjects = scene.Value.GetRootGameObjects(); 38 | 39 | foreach (GameObject rootGameObject in rootGameObjects) 40 | { 41 | CreateItemsRecursive(rootGameObject.transform, root); 42 | } 43 | 44 | return root; 45 | } 46 | 47 | private void CreateItemsRecursive(Transform transform, AdvancedDropdownItem parent) 48 | { 49 | AdvancedDropdownItem advancedDropdownItem = new AdvancedDropdownItem(transform.name) 50 | { 51 | icon = IconUtility.GameObjectIcon 52 | }; 53 | 54 | Component[] components = transform.GetComponents(interfaceType); 55 | 56 | foreach (Component component in components) 57 | { 58 | advancedDropdownItem.AddChild(new SceneDropdownItem(component)); 59 | } 60 | 61 | foreach (Transform child in transform) 62 | { 63 | CreateItemsRecursive(child, advancedDropdownItem); 64 | } 65 | 66 | if (advancedDropdownItem.children.Any()) 67 | parent.AddChild(advancedDropdownItem); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Editor/Builders/SceneItemBuilder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 54def1314fae491681e63e0cbb839b9b 3 | timeCreated: 1650789279 -------------------------------------------------------------------------------- /Editor/Drawers.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 092a8a00426d46aba03d829da3c59d8e 3 | timeCreated: 1651172005 -------------------------------------------------------------------------------- /Editor/Drawers/CustomObjectDrawer.Styles.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEngine; 3 | 4 | namespace TNRD.Drawers 5 | { 6 | public partial class CustomObjectDrawer 7 | { 8 | private static class Styles 9 | { 10 | private static GUIStyle regularLabelStyle; 11 | private static GUIStyle selectedLabelStyle; 12 | 13 | public static GUIStyle RegularLabelStyle => regularLabelStyle ??= new GUIStyle(EditorStyles.label); 14 | 15 | public static GUIStyle SelectedLabelStyle 16 | { 17 | get 18 | { 19 | if (selectedLabelStyle == null) 20 | { 21 | selectedLabelStyle = new GUIStyle(EditorStyles.label) 22 | { 23 | normal = 24 | { 25 | textColor = EditorGUIUtility.isProSkin 26 | ? new Color32(128, 179, 253, 255) 27 | : new Color32(18, 73, 142, 255) 28 | }, 29 | hover = 30 | { 31 | textColor = EditorGUIUtility.isProSkin 32 | ? new Color32(128, 179, 253, 255) 33 | : new Color32(18, 73, 142, 255) 34 | } 35 | }; 36 | } 37 | 38 | return selectedLabelStyle; 39 | } 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Editor/Drawers/CustomObjectDrawer.Styles.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dca8b4d8187c486bae8950c504dde4e4 3 | timeCreated: 1651176627 -------------------------------------------------------------------------------- /Editor/Drawers/CustomObjectDrawer.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_2022_3_20 || UNITY_2022_3_21 2 | #define UNITY_2022_3_20_OR_NEWER 3 | using TNRD.Utilities; 4 | #endif 5 | using TNRD.Utilities; 6 | using UnityEditor; 7 | using UnityEngine; 8 | 9 | namespace TNRD.Drawers 10 | { 11 | public partial class CustomObjectDrawer 12 | { 13 | public delegate void ButtonClickedDelegate(Rect position, SerializedProperty property); 14 | 15 | public delegate void ClickedDelegate(SerializedProperty property); 16 | 17 | public delegate void DeletePressedDelegate(SerializedProperty property); 18 | 19 | public delegate void PropertiesClickedDelegate(SerializedProperty property); 20 | 21 | private bool isSelected; 22 | 23 | private Event Event => Event.current; 24 | 25 | public event ButtonClickedDelegate ButtonClicked; 26 | public event ClickedDelegate Clicked; 27 | public event DeletePressedDelegate DeletePressed; 28 | public event PropertiesClickedDelegate PropertiesClicked; 29 | 30 | public void OnGUI(Rect position, GUIContent label, GUIContent content, SerializedProperty property, bool hasChildren) 31 | { 32 | Rect positionWithoutThumb = new Rect(position); 33 | positionWithoutThumb.xMax -= 20; 34 | 35 | position = DrawPrefixLabel(position, label, hasChildren); 36 | DrawObjectField(position, content); 37 | DrawButton(position, property); 38 | 39 | HandleMouseDown(position, positionWithoutThumb, property); 40 | HandleKeyDown(property); 41 | } 42 | 43 | private Rect DrawPrefixLabel(Rect position, GUIContent label, bool hasChildren) 44 | { 45 | #if UNITY_2022_3_20_OR_NEWER 46 | GUIStyle labelStyle = isSelected ? Styles.SelectedLabelStyle : Styles.RegularLabelStyle; 47 | 48 | if (hasChildren || EditorGUI.indentLevel >= 1) 49 | { 50 | using (new EditorGUI.IndentLevelScope(-1)) 51 | { 52 | position.xMin -= ReflectedEditorGUI.indent; 53 | } 54 | } 55 | 56 | Rect result = EditorGUI.PrefixLabel(position, label, labelStyle); 57 | 58 | if (hasChildren || EditorGUI.indentLevel >= 1) 59 | { 60 | result.xMin -= ReflectedEditorGUI.indentWidth; 61 | } 62 | 63 | return result; 64 | #elif UNITY_2022_2_OR_NEWER 65 | if (hasChildren || EditorGUI.indentLevel >= 1) 66 | { 67 | position.xMin += ReflectedEditorGUI.indentWidth; 68 | } 69 | 70 | GUIStyle labelStyle = isSelected ? Styles.SelectedLabelStyle : Styles.RegularLabelStyle; 71 | Rect result = EditorGUI.PrefixLabel(position, label, labelStyle); 72 | if (hasChildren || EditorGUI.indentLevel>=1) 73 | result.xMin -= ReflectedEditorGUI.indentWidth; 74 | return result; 75 | #else 76 | GUIStyle labelStyle = isSelected ? Styles.SelectedLabelStyle : Styles.RegularLabelStyle; 77 | Rect result = EditorGUI.PrefixLabel(position, label, labelStyle); 78 | return result; 79 | #endif 80 | } 81 | 82 | private void DrawObjectField(Rect position, GUIContent objectFieldContent) 83 | { 84 | Rect positionWithoutThumb = new Rect(position); 85 | positionWithoutThumb.xMax -= 20; 86 | 87 | if (Event.type == EventType.Repaint) 88 | { 89 | EditorStyles.objectField.Draw(position, 90 | objectFieldContent, 91 | position.Contains(Event.mousePosition), 92 | false, 93 | false, 94 | isSelected); 95 | } 96 | } 97 | 98 | private void ForceRepaintEditors() 99 | { 100 | foreach (Editor activeEditor in ActiveEditorTracker.sharedTracker.activeEditors) 101 | { 102 | activeEditor.Repaint(); 103 | } 104 | } 105 | 106 | private void DrawButton(Rect position, SerializedProperty property) 107 | { 108 | Rect buttonRect = new Rect(position); 109 | buttonRect.yMin += 1; 110 | buttonRect.yMax -= 1; 111 | buttonRect.xMin = buttonRect.xMax - 20; 112 | buttonRect.xMax -= 1; 113 | 114 | if (GUI.Button(buttonRect, string.Empty, "objectFieldButton")) 115 | { 116 | ButtonClicked?.Invoke(position, property); 117 | } 118 | } 119 | 120 | private void HandleMouseDown(Rect position, Rect positionWithoutThumb, SerializedProperty property) 121 | { 122 | if (Event.type != EventType.MouseDown) 123 | return; 124 | 125 | if (Event.button == 0) 126 | { 127 | isSelected = positionWithoutThumb.Contains(Event.mousePosition); 128 | ForceRepaintEditors(); 129 | if (isSelected) 130 | { 131 | Clicked?.Invoke(property); 132 | } 133 | } 134 | else if (Event.button == 1 && positionWithoutThumb.Contains(Event.mousePosition)) 135 | { 136 | GenericMenu menu = new GenericMenu(); 137 | menu.AddItem(new GUIContent("Clear"), false, () => { DeletePressed?.Invoke(property); }); 138 | menu.AddItem(new GUIContent("Properties..."), false, () => { PropertiesClicked?.Invoke(property); }); 139 | menu.DropDown(position); 140 | Event.Use(); 141 | } 142 | } 143 | 144 | private void HandleKeyDown(SerializedProperty property) 145 | { 146 | if (!isSelected) 147 | return; 148 | 149 | if (Event.type == EventType.KeyDown && Event.keyCode == KeyCode.Delete) 150 | { 151 | DeletePressed?.Invoke(property); 152 | } 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /Editor/Drawers/CustomObjectDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d6ed901070c64bfcaa7de131652bdd40 3 | timeCreated: 1651172254 -------------------------------------------------------------------------------- /Editor/Drawers/IReferenceDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace TNRD.Drawers 4 | { 5 | internal interface IReferenceDrawer 6 | { 7 | float GetHeight(); 8 | void OnGUI(Rect position); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Editor/Drawers/IReferenceDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c88f02b7e9174967b1bc0670049f006f 3 | timeCreated: 1651174294 -------------------------------------------------------------------------------- /Editor/Drawers/RawReferenceDrawer.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_2022_3_20 || UNITY_2022_3_21 2 | #define UNITY_2022_3_20_OR_NEWER 3 | #endif 4 | 5 | using System; 6 | using System.Reflection; 7 | using TNRD.Utilities; 8 | using UnityEditor; 9 | using UnityEngine; 10 | 11 | namespace TNRD.Drawers 12 | { 13 | internal partial class RawReferenceDrawer : ReferenceDrawer, IReferenceDrawer 14 | { 15 | private GUIContent label; 16 | private FieldInfo fieldInfo; 17 | 18 | private object previousReferenceValue; 19 | private string previousPropertyPath; 20 | 21 | public void Initialize(SerializedProperty property, Type genericType, GUIContent label, FieldInfo fieldInfo) 22 | { 23 | Initialize(property, genericType, fieldInfo); 24 | this.label = label; 25 | } 26 | 27 | /// 28 | public float GetHeight() 29 | { 30 | if (RawReferenceValue == null) 31 | return EditorGUIUtility.singleLineHeight; 32 | 33 | return EditorGUI.GetPropertyHeight(RawReferenceProperty, true); 34 | } 35 | 36 | /// 37 | public void OnGUI(Rect position) 38 | { 39 | AvoidDuplicateReferencesInArray(); 40 | 41 | Rect objectFieldRect = new Rect(position) 42 | { 43 | height = EditorGUIUtility.singleLineHeight 44 | }; 45 | 46 | object rawReferenceValue = RawReferenceValue; 47 | 48 | GUIContent content = rawReferenceValue == null 49 | ? EditorGUIUtility.ObjectContent((MonoScript)null, typeof(MonoScript)) 50 | : new GUIContent(rawReferenceValue.GetType().Name, IconUtility.ScriptIcon); 51 | 52 | CustomObjectDrawer.OnGUI(objectFieldRect, label, content, Property, rawReferenceValue != null); 53 | 54 | HandleDragAndDrop(objectFieldRect); 55 | 56 | if (rawReferenceValue == null) 57 | return; 58 | 59 | DrawProperty(position); 60 | previousPropertyPath = Property.propertyPath; 61 | } 62 | 63 | private void DrawProperty(Rect position) 64 | { 65 | #if UNITY_2022_3_20_OR_NEWER 66 | position.xMin -= ReflectedEditorGUI.indent; 67 | #endif 68 | EditorGUI.PropertyField(position, 69 | RawReferenceProperty, 70 | GUIContent.none, 71 | true); 72 | } 73 | 74 | protected override void PingObject(SerializedProperty property) 75 | { 76 | // No support for pinging raw objects for now (I guess this would ping the MonoScript?) 77 | } 78 | 79 | /// 80 | protected override void OnPropertiesClicked(SerializedProperty property) 81 | { 82 | if (RawReferenceValue == null) 83 | return; 84 | 85 | Type type = RawReferenceValue.GetType(); 86 | string typeName = type.Name; 87 | 88 | string[] guids = AssetDatabase.FindAssets($"t:Script {typeName}"); 89 | foreach (string guid in guids) 90 | { 91 | string assetPath = AssetDatabase.GUIDToAssetPath(guid); 92 | MonoScript monoScript = AssetDatabase.LoadAssetAtPath(assetPath); 93 | if (monoScript.GetClass() == type) 94 | { 95 | PropertyEditorUtility.Show(monoScript); 96 | return; 97 | } 98 | } 99 | } 100 | 101 | private void AvoidDuplicateReferencesInArray() 102 | { 103 | if (!ShouldCheckProperty()) 104 | return; 105 | 106 | object currentReferenceValue = RawReferenceValue; 107 | 108 | if (currentReferenceValue == null) 109 | return; 110 | 111 | if (previousReferenceValue == currentReferenceValue) 112 | { 113 | currentReferenceValue = CreateInstance(currentReferenceValue); 114 | PropertyValue = currentReferenceValue; 115 | } 116 | 117 | previousReferenceValue = currentReferenceValue; 118 | } 119 | 120 | private bool ShouldCheckProperty() 121 | { 122 | return IsPropertyInArray(Property) && 123 | previousPropertyPath != null && 124 | previousPropertyPath != Property.propertyPath; 125 | } 126 | 127 | private static bool IsPropertyInArray(SerializedProperty prop) 128 | { 129 | return prop.propertyPath.Contains(".Array.data["); 130 | } 131 | 132 | private static object CreateInstance(object source) 133 | { 134 | object instance = Activator.CreateInstance(source.GetType()); 135 | EditorUtility.CopySerializedManagedFieldsOnly(source, instance); 136 | return instance; 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Editor/Drawers/RawReferenceDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fe6e67367395478ea805d0231da3a352 3 | timeCreated: 1651174357 -------------------------------------------------------------------------------- /Editor/Drawers/ReferenceDrawer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using TNRD.Utilities; 5 | using UnityEditor; 6 | using UnityEditor.IMGUI.Controls; 7 | using UnityEngine; 8 | using UnityEngine.Assertions; 9 | using UnityEngine.SceneManagement; 10 | using Object = UnityEngine.Object; 11 | 12 | namespace TNRD.Drawers 13 | { 14 | internal abstract class ReferenceDrawer 15 | { 16 | private enum DragAndDropMode 17 | { 18 | None, 19 | Raw, 20 | Unity 21 | } 22 | 23 | private DragAndDropMode dragAndDropMode; 24 | 25 | protected readonly CustomObjectDrawer CustomObjectDrawer; 26 | 27 | protected SerializedProperty Property { get; private set; } 28 | protected Type GenericType { get; private set; } 29 | 30 | protected SerializedProperty ReferenceModeProperty => Property.ReferenceModeProperty(); 31 | protected SerializedProperty RawReferenceProperty => Property.RawReferenceProperty(); 32 | protected SerializedProperty UnityReferenceProperty => Property.UnityReferenceProperty(); 33 | 34 | protected FieldInfo FieldInfo { get; private set; } 35 | 36 | protected ReferenceMode ModeValue 37 | { 38 | get => GetModeValue(Property); 39 | set => SetModeValue(Property, value); 40 | } 41 | 42 | protected object RawReferenceValue 43 | { 44 | get => GetRawReferenceValue(Property); 45 | set => SetRawReferenceValue(Property, value); 46 | } 47 | 48 | protected object PropertyValue 49 | { 50 | get => GetPropertyValue(Property); 51 | set => SetPropertyValue(Property, value); 52 | } 53 | 54 | protected ReferenceDrawer() 55 | { 56 | CustomObjectDrawer = new CustomObjectDrawer(); 57 | CustomObjectDrawer.ButtonClicked += OnButtonClicked; 58 | CustomObjectDrawer.Clicked += OnClicked; 59 | CustomObjectDrawer.DeletePressed += OnDeletePressed; 60 | CustomObjectDrawer.PropertiesClicked += OnPropertiesClicked; 61 | } 62 | 63 | protected void Initialize(SerializedProperty property, Type genericType, FieldInfo fieldInfo) 64 | { 65 | Property = property; 66 | GenericType = genericType; 67 | FieldInfo = fieldInfo; 68 | } 69 | 70 | private void OnButtonClicked(Rect position, SerializedProperty property) 71 | { 72 | AdvancedDropdownState state = new AdvancedDropdownState(); 73 | SerializableInterfaceAdvancedDropdown dropdown = 74 | new SerializableInterfaceAdvancedDropdown(state, GenericType, GetRelevantScene(property), property, FieldInfo.GetCustomAttribute() != null); 75 | dropdown.ItemSelectedEvent += OnItemSelected; 76 | dropdown.Show(position); 77 | } 78 | 79 | private static Scene? GetRelevantScene(SerializedProperty property) 80 | { 81 | Object target = property.serializedObject.targetObject; 82 | 83 | if (target is ScriptableObject) 84 | return null; 85 | if (target is Component component) 86 | return component.gameObject.scene; 87 | if (target is GameObject gameObject) 88 | return gameObject.scene; 89 | 90 | return null; 91 | } 92 | 93 | private void OnClicked(SerializedProperty property) 94 | { 95 | PingObject(property); 96 | } 97 | 98 | private void OnDeletePressed(SerializedProperty property) 99 | { 100 | SetModeValue(property, default); 101 | SetPropertyValue(property, null); 102 | } 103 | 104 | private void OnItemSelected(SerializedProperty property, ReferenceMode mode, object reference) 105 | { 106 | SetModeValue(property, mode); 107 | SetPropertyValue(property, reference); 108 | } 109 | 110 | protected abstract void OnPropertiesClicked(SerializedProperty property); 111 | 112 | protected void HandleDragAndDrop(Rect position) 113 | { 114 | if (!position.Contains(Event.current.mousePosition)) 115 | return; 116 | 117 | if (Event.current.type == EventType.DragPerform) 118 | { 119 | HandleDragUpdated(); 120 | HandleDragPerform(); 121 | } 122 | else if (Event.current.type == EventType.DragUpdated) 123 | { 124 | HandleDragUpdated(); 125 | } 126 | } 127 | 128 | private void SetDragAndDropMode(bool success, DragAndDropMode? successMode = null) 129 | { 130 | if (success) 131 | { 132 | Assert.IsTrue(successMode.HasValue); 133 | dragAndDropMode = successMode.Value; 134 | DragAndDrop.visualMode = DragAndDropVisualMode.Link; 135 | } 136 | else 137 | { 138 | dragAndDropMode = DragAndDropMode.None; 139 | DragAndDrop.visualMode = DragAndDropVisualMode.Rejected; 140 | } 141 | } 142 | 143 | private void HandleDragUpdated() 144 | { 145 | if (DragAndDrop.objectReferences.Length > 1) 146 | { 147 | SetDragAndDropMode(false); 148 | return; 149 | } 150 | 151 | Object objectReference = DragAndDrop.objectReferences[0]; 152 | 153 | if (objectReference is GameObject gameObject) 154 | { 155 | Component component = gameObject.GetComponent(GenericType); 156 | SetDragAndDropMode(component != null, DragAndDropMode.Unity); 157 | } 158 | else if (objectReference is MonoScript monoScript) 159 | { 160 | Type scriptType = monoScript.GetClass(); 161 | 162 | if (scriptType.IsSubclassOf(typeof(Object))) 163 | { 164 | SetDragAndDropMode(false); 165 | return; 166 | } 167 | 168 | if (!GenericType.IsAssignableFrom(scriptType)) 169 | { 170 | SetDragAndDropMode(false); 171 | return; 172 | } 173 | 174 | bool isValidDrop = scriptType.GetConstructors().Any(x => x.GetParameters().Length == 0); 175 | SetDragAndDropMode(isValidDrop, DragAndDropMode.Raw); 176 | } 177 | else 178 | { 179 | SetDragAndDropMode(GenericType.IsInstanceOfType(objectReference), DragAndDropMode.Unity); 180 | } 181 | } 182 | 183 | private void HandleDragPerform() 184 | { 185 | switch (dragAndDropMode) 186 | { 187 | case DragAndDropMode.Raw: 188 | ModeValue = ReferenceMode.Raw; 189 | PropertyValue = Activator.CreateInstance(((MonoScript)DragAndDrop.objectReferences[0]).GetClass()); 190 | break; 191 | case DragAndDropMode.Unity: 192 | ModeValue = ReferenceMode.Unity; 193 | PropertyValue = DragAndDrop.objectReferences[0]; 194 | break; 195 | case DragAndDropMode.None: 196 | default: 197 | throw new ArgumentOutOfRangeException(); 198 | } 199 | } 200 | 201 | private Object GetUnityObject(Object objectReference) 202 | { 203 | if (objectReference is GameObject gameObject) 204 | return gameObject.GetComponent(GenericType); 205 | return objectReference; 206 | } 207 | 208 | protected abstract void PingObject(SerializedProperty property); 209 | 210 | protected ReferenceMode GetModeValue(SerializedProperty property) 211 | { 212 | return (ReferenceMode)property.ReferenceModeProperty().enumValueIndex; 213 | } 214 | 215 | protected void SetModeValue(SerializedProperty property, ReferenceMode mode) 216 | { 217 | property.ReferenceModeProperty().enumValueIndex = (int)mode; 218 | } 219 | 220 | protected object GetRawReferenceValue(SerializedProperty property) 221 | { 222 | #if UNITY_2021_2_OR_NEWER 223 | return property.RawReferenceProperty().managedReferenceValue; 224 | #else 225 | ISerializableInterface target = (ISerializableInterface) SerializedPropertyUtilities.GetValue(property); 226 | return target.GetRawReference(); 227 | #endif 228 | } 229 | 230 | protected void SetRawReferenceValue(SerializedProperty property, object value) 231 | { 232 | #if UNITY_2021_1_OR_NEWER 233 | property.RawReferenceProperty().managedReferenceValue = value; 234 | #else 235 | FieldInfo.SetValue(property.serializedObject.targetObject, value); 236 | #endif 237 | } 238 | 239 | protected Object GetUnityReferenceValue(SerializedProperty property) 240 | { 241 | return property.UnityReferenceProperty().objectReferenceValue; 242 | } 243 | 244 | protected void SetUnityReferenceValue(SerializedProperty property, object value) 245 | { 246 | property.UnityReferenceProperty().objectReferenceValue = GetUnityObject((Object)value); 247 | } 248 | 249 | protected object GetPropertyValue(SerializedProperty property) 250 | { 251 | return GetModeValue(property) switch 252 | { 253 | ReferenceMode.Raw => GetRawReferenceValue(property), 254 | ReferenceMode.Unity => GetUnityReferenceValue(property), 255 | _ => throw new ArgumentOutOfRangeException() 256 | }; 257 | } 258 | 259 | protected void SetPropertyValue(SerializedProperty property, object value) 260 | { 261 | switch (GetModeValue(property)) 262 | { 263 | case ReferenceMode.Unity: 264 | SetUnityReferenceValue(property, value); 265 | SetRawReferenceValue(property, null); 266 | break; 267 | case ReferenceMode.Raw: 268 | SetRawReferenceValue(property, value); 269 | SetUnityReferenceValue(property, null); 270 | break; 271 | default: 272 | throw new ArgumentOutOfRangeException(); 273 | } 274 | 275 | property.serializedObject.ApplyModifiedProperties(); 276 | } 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /Editor/Drawers/ReferenceDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c1803d06a9a64d43ae15f1a5b898ae15 3 | timeCreated: 1651174301 -------------------------------------------------------------------------------- /Editor/Drawers/UnityReferenceDrawer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using TNRD.Utilities; 4 | using UnityEditor; 5 | using UnityEngine; 6 | using Object = UnityEngine.Object; 7 | 8 | namespace TNRD.Drawers 9 | { 10 | internal class UnityReferenceDrawer : ReferenceDrawer, IReferenceDrawer 11 | { 12 | private GUIContent label; 13 | 14 | public void Initialize(SerializedProperty property, Type genericType, GUIContent label, FieldInfo fieldInfo) 15 | { 16 | Initialize(property, genericType, fieldInfo); 17 | this.label = label; 18 | } 19 | 20 | public float GetHeight() 21 | { 22 | return EditorGUIUtility.singleLineHeight; 23 | } 24 | 25 | public void OnGUI(Rect position) 26 | { 27 | Object unityReference = UnityReferenceProperty.objectReferenceValue; 28 | Type referenceType = unityReference == null ? typeof(Object) : unityReference.GetType(); 29 | GUIContent objectContent = EditorGUIUtility.ObjectContent(unityReference, referenceType); 30 | CustomObjectDrawer.OnGUI(position, label, objectContent, Property, false); 31 | HandleDragAndDrop(position); 32 | } 33 | 34 | protected override void PingObject(SerializedProperty property) 35 | { 36 | EditorGUIUtility.PingObject((Object)GetPropertyValue(property)); 37 | } 38 | 39 | protected override void OnPropertiesClicked(SerializedProperty property) 40 | { 41 | PropertyEditorUtility.Show(property.UnityReferenceProperty().objectReferenceValue); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Editor/Drawers/UnityReferenceDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 563fb2f2a8904b80bebd2da80d1a9a23 3 | timeCreated: 1651172014 -------------------------------------------------------------------------------- /Editor/Items.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6b5c9abc75da481f8f765a5a7fd0396a 3 | timeCreated: 1650798832 -------------------------------------------------------------------------------- /Editor/Items/AssetDropdownItem.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using TNRD.Utilities; 3 | using UnityEditor; 4 | using UnityEditor.IMGUI.Controls; 5 | using UnityEngine; 6 | 7 | namespace TNRD.Items 8 | { 9 | internal sealed class AssetDropdownItem : AdvancedDropdownItem, IDropdownItem 10 | { 11 | private readonly string path; 12 | 13 | /// 14 | public AssetDropdownItem(string path) 15 | : base(Path.GetFileNameWithoutExtension(path)) 16 | { 17 | this.path = path; 18 | icon = IconUtility.GetIconForObject(path); 19 | } 20 | 21 | /// 22 | ReferenceMode IDropdownItem.Mode => ReferenceMode.Unity; 23 | 24 | /// 25 | object IDropdownItem.GetValue() 26 | { 27 | return AssetDatabase.LoadAssetAtPath(path); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Editor/Items/AssetDropdownItem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a75982f123bb418eaed87cacb93db8d0 3 | timeCreated: 1650798840 -------------------------------------------------------------------------------- /Editor/Items/ClassDropdownItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using TNRD.Utilities; 4 | using UnityEditor.IMGUI.Controls; 5 | 6 | namespace TNRD.Items 7 | { 8 | internal sealed class ClassDropdownItem : AdvancedDropdownItem, IDropdownItem 9 | { 10 | private readonly Type type; 11 | 12 | /// 13 | public ClassDropdownItem(Type type) 14 | : base(type.Name) 15 | { 16 | this.type = type; 17 | enabled = type.GetConstructors().Any(x => x.GetParameters().Length == 0); 18 | icon = IconUtility.ScriptIcon; 19 | } 20 | 21 | /// 22 | ReferenceMode IDropdownItem.Mode => ReferenceMode.Raw; 23 | 24 | /// 25 | object IDropdownItem.GetValue() 26 | { 27 | return Activator.CreateInstance(type); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Editor/Items/ClassDropdownItem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e42657e946b34cc693758964abdb1565 3 | timeCreated: 1650798853 -------------------------------------------------------------------------------- /Editor/Items/IDropdownItem.cs: -------------------------------------------------------------------------------- 1 | namespace TNRD.Items 2 | { 3 | internal interface IDropdownItem 4 | { 5 | internal ReferenceMode Mode { get; } 6 | object GetValue(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Editor/Items/IDropdownItem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 700554e53cad46a8a6d28a2162854323 3 | timeCreated: 1650799589 -------------------------------------------------------------------------------- /Editor/Items/NoneDropdownItem.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor.IMGUI.Controls; 2 | 3 | namespace TNRD.Items 4 | { 5 | public class NoneDropdownItem : AdvancedDropdownItem, IDropdownItem 6 | { 7 | public NoneDropdownItem() 8 | : base("None") 9 | { 10 | } 11 | 12 | ReferenceMode IDropdownItem.Mode => ReferenceMode.Raw; 13 | 14 | public object GetValue() 15 | { 16 | return null; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Editor/Items/NoneDropdownItem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a7015062205d46cbb9cb01b0b88fff55 3 | timeCreated: 1657572673 -------------------------------------------------------------------------------- /Editor/Items/SceneDropdownItem.cs: -------------------------------------------------------------------------------- 1 | using TNRD.Utilities; 2 | using UnityEditor.IMGUI.Controls; 3 | using UnityEngine; 4 | 5 | namespace TNRD.Items 6 | { 7 | internal sealed class SceneDropdownItem : AdvancedDropdownItem, IDropdownItem 8 | { 9 | private readonly Object component; 10 | 11 | /// 12 | public SceneDropdownItem(Component component) 13 | : base(component.GetType().Name) 14 | { 15 | this.component = component; 16 | icon = IconUtility.GetIconForObject(component) ?? IconUtility.ScriptIcon; 17 | } 18 | 19 | /// 20 | ReferenceMode IDropdownItem.Mode => ReferenceMode.Unity; 21 | 22 | /// 23 | object IDropdownItem.GetValue() 24 | { 25 | return component; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Editor/Items/SceneDropdownItem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1288901665e442108e0a55d25f6d62e8 3 | timeCreated: 1650798864 -------------------------------------------------------------------------------- /Editor/SerializableInterfacePropertyDrawer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using TNRD.Drawers; 4 | using TNRD.Utilities; 5 | using UnityEditor; 6 | using UnityEngine; 7 | using UnityEngine.Assertions; 8 | 9 | namespace TNRD 10 | { 11 | [CustomPropertyDrawer(typeof(SerializableInterface<>), true)] 12 | internal sealed class SerializableInterfacePropertyDrawer : PropertyDrawer 13 | { 14 | private readonly RawReferenceDrawer rawReferenceDrawer = new RawReferenceDrawer(); 15 | private readonly UnityReferenceDrawer unityReferenceDrawer = new UnityReferenceDrawer(); 16 | 17 | private SerializedProperty serializedProperty; 18 | private Type genericType; 19 | 20 | /// 21 | public override bool CanCacheInspectorGUI(SerializedProperty property) => false; 22 | 23 | private void Initialize(SerializedProperty property) 24 | { 25 | if (serializedProperty == property) 26 | return; 27 | 28 | serializedProperty = property; 29 | genericType = GetGenericArgument(); 30 | Assert.IsNotNull(genericType, "Unable to find generic argument, are you doing some shady inheritance?"); 31 | } 32 | 33 | /// 34 | public override float GetPropertyHeight(SerializedProperty property, GUIContent label) 35 | { 36 | Initialize(property); 37 | return GetReferenceDrawer(property, label).GetHeight(); 38 | } 39 | 40 | /// 41 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 42 | { 43 | Initialize(property); 44 | GetReferenceDrawer(property, label).OnGUI(position); 45 | } 46 | 47 | private Type GetGenericArgument() 48 | { 49 | Type type = fieldInfo.FieldType; 50 | 51 | while (type != null) 52 | { 53 | if (type.IsArray) 54 | type = type.GetElementType(); 55 | 56 | if (type == null) 57 | return null; 58 | 59 | if (type.IsGenericType) 60 | { 61 | if (typeof(IEnumerable).IsAssignableFrom(type)) 62 | { 63 | type = type.GetGenericArguments()[0]; 64 | } 65 | else if (type.GetGenericTypeDefinition() == typeof(SerializableInterface<>)) 66 | { 67 | return type.GetGenericArguments()[0]; 68 | } 69 | else 70 | { 71 | type = type.BaseType; 72 | } 73 | } 74 | else 75 | { 76 | type = type.BaseType; 77 | } 78 | } 79 | 80 | return null; 81 | } 82 | 83 | private IReferenceDrawer GetReferenceDrawer(SerializedProperty property, GUIContent label) 84 | { 85 | SerializedProperty modeProperty = serializedProperty.ReferenceModeProperty(); 86 | ReferenceMode referenceMode = (ReferenceMode)modeProperty.enumValueIndex; 87 | 88 | switch (referenceMode) 89 | { 90 | case ReferenceMode.Raw: 91 | rawReferenceDrawer.Initialize(property, genericType, label, fieldInfo); 92 | return rawReferenceDrawer; 93 | case ReferenceMode.Unity: 94 | unityReferenceDrawer.Initialize(property, genericType, label, fieldInfo); 95 | return unityReferenceDrawer; 96 | default: 97 | throw new ArgumentOutOfRangeException(); 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Editor/SerializableInterfacePropertyDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f2aed610bd6a40a1ab634ffc58ef4b56 3 | timeCreated: 1650737626 -------------------------------------------------------------------------------- /Editor/TNRD.SerializableInterface.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TNRD.SerializableInterface.Editor", 3 | "rootNamespace": "TNRD", 4 | "references": [ 5 | "TNRD.SerializableInterface" 6 | ], 7 | "includePlatforms": ["Editor"], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": false, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [], 14 | "versionDefines": [], 15 | "noEngineReferences": false 16 | } -------------------------------------------------------------------------------- /Editor/TNRD.SerializableInterface.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7017cf83799d50242a4cf38dd4d9fdbf 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Utilities.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9e7b552930b34af48eefd93242d32f94 3 | timeCreated: 1650795731 -------------------------------------------------------------------------------- /Editor/Utilities/AdvancedDropdownItemWrapper.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor.IMGUI.Controls; 2 | 3 | namespace TNRD.Utilities 4 | { 5 | internal sealed class AdvancedDropdownItemWrapper : AdvancedDropdownItem 6 | { 7 | /// 8 | public AdvancedDropdownItemWrapper(string name) 9 | : base(name) 10 | { 11 | } 12 | 13 | public new AdvancedDropdownItemWrapper AddChild(AdvancedDropdownItem child) 14 | { 15 | base.AddChild(child); 16 | return this; 17 | } 18 | 19 | public new AdvancedDropdownItemWrapper AddSeparator() 20 | { 21 | base.AddSeparator(); 22 | return this; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Editor/Utilities/AdvancedDropdownItemWrapper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 99ff0652cd774ec791e8b3115aaf6ba7 3 | timeCreated: 1650738177 -------------------------------------------------------------------------------- /Editor/Utilities/IconUtility.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEngine; 3 | 4 | namespace TNRD.Utilities 5 | { 6 | internal static class IconUtility 7 | { 8 | #if UNITY_2021_2_OR_NEWER 9 | private static Texture2D folderIcon; 10 | 11 | public static Texture2D FolderIcon 12 | { 13 | get 14 | { 15 | if (folderIcon == null) 16 | folderIcon = (Texture2D)EditorGUIUtility.IconContent("Folder Icon").image; 17 | 18 | return folderIcon; 19 | } 20 | } 21 | 22 | private static Texture2D scriptIcon; 23 | 24 | public static Texture2D ScriptIcon 25 | { 26 | get 27 | { 28 | if (scriptIcon == null) 29 | scriptIcon = (Texture2D)EditorGUIUtility.IconContent("cs Script Icon").image; 30 | 31 | return scriptIcon; 32 | } 33 | } 34 | 35 | private static Texture2D gameObjectIcon; 36 | 37 | public static Texture2D GameObjectIcon 38 | { 39 | get 40 | { 41 | if (gameObjectIcon == null) 42 | gameObjectIcon = (Texture2D)EditorGUIUtility.IconContent("GameObject Icon").image; 43 | 44 | return gameObjectIcon; 45 | } 46 | } 47 | 48 | 49 | public static Texture2D GetIconForObject(string path) 50 | { 51 | return (Texture2D)AssetDatabase.GetCachedIcon(path); 52 | } 53 | 54 | public static Texture2D GetIconForObject(Object obj) 55 | { 56 | return EditorGUIUtility.GetIconForObject(obj); 57 | } 58 | #else 59 | // "Fix" for 2020 as the icons are 256x256 in size and the AdvancedDropDown fills the space as much as it can 60 | public static Texture2D FolderIcon => null; 61 | 62 | private static Texture2D scriptIcon; 63 | 64 | public static Texture2D ScriptIcon 65 | { 66 | get 67 | { 68 | if (scriptIcon == null) 69 | scriptIcon = (Texture2D)EditorGUIUtility.IconContent("cs Script Icon").image; 70 | 71 | return scriptIcon; 72 | } 73 | } 74 | 75 | public static Texture2D GameObjectIcon => null; 76 | 77 | public static Texture2D GetIconForObject(string path) 78 | { 79 | return null; 80 | } 81 | 82 | public static Texture2D GetIconForObject(Object obj) 83 | { 84 | return null; 85 | } 86 | #endif 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Editor/Utilities/IconUtility.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8b5aa8796b0b447f8243612af7792ec7 3 | timeCreated: 1650796270 -------------------------------------------------------------------------------- /Editor/Utilities/PropertyEditorUtility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using UnityEditor; 5 | using Object = UnityEngine.Object; 6 | 7 | namespace TNRD.Utilities 8 | { 9 | internal static class PropertyEditorUtility 10 | { 11 | internal static void Show(Object obj) 12 | { 13 | Type propertyEditor = typeof(EditorWindow).Assembly.GetTypes() 14 | .FirstOrDefault(x => x.Name == "PropertyEditor"); 15 | 16 | if (propertyEditor == null) 17 | return; 18 | 19 | MethodInfo openPropertyEditorMethod = propertyEditor.GetMethod("OpenPropertyEditor", 20 | BindingFlags.Static | BindingFlags.NonPublic, 21 | null, 22 | new Type[] 23 | { 24 | typeof(UnityEngine.Object), 25 | typeof(bool) 26 | }, 27 | null); 28 | 29 | openPropertyEditorMethod.Invoke(null, 30 | new object[] 31 | { 32 | obj, true 33 | }); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Editor/Utilities/PropertyEditorUtility.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d46a0124097c4151a2ee2a688ddc0b08 3 | timeCreated: 1650822446 -------------------------------------------------------------------------------- /Editor/Utilities/ReflectedEditorGUI.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using UnityEditor; 3 | 4 | namespace TNRD.Utilities 5 | { 6 | public static class ReflectedEditorGUI 7 | { 8 | private static readonly PropertyInfo indentLevelProperty = 9 | typeof(EditorGUI).GetProperty("indent", BindingFlags.Static | BindingFlags.NonPublic); 10 | 11 | private static readonly FieldInfo indentWidthField = 12 | typeof(EditorGUI).GetField("kIndentPerLevel", BindingFlags.Static | BindingFlags.NonPublic); 13 | 14 | static ReflectedEditorGUI() 15 | { 16 | indentWidth = (float)indentWidthField.GetValue(null); 17 | } 18 | 19 | public static float indent => (float)indentLevelProperty.GetValue(null); 20 | 21 | public static float indentWidth { get; private set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Editor/Utilities/ReflectedEditorGUI.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2f90b87b0e7f42e784ca7266c0b4163e 3 | timeCreated: 1710599777 -------------------------------------------------------------------------------- /Editor/Utilities/SerializableInterfaceAdvancedDropdown.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using TNRD.Builders; 5 | using TNRD.Items; 6 | using UnityEditor; 7 | using UnityEditor.IMGUI.Controls; 8 | using UnityEngine; 9 | using UnityEngine.Assertions; 10 | using UnityEngine.SceneManagement; 11 | 12 | namespace TNRD.Utilities 13 | { 14 | internal sealed class SerializableInterfaceAdvancedDropdown : AdvancedDropdown 15 | { 16 | private readonly Type interfaceType; 17 | private readonly MethodInfo sortChildrenMethod; 18 | private readonly bool canSort; 19 | private readonly bool classesOnly; 20 | private readonly Scene? relevantScene; 21 | private readonly SerializedProperty property; 22 | 23 | public delegate void ItemSelectedDelegate(SerializedProperty property, ReferenceMode mode, object reference); 24 | 25 | public event ItemSelectedDelegate ItemSelectedEvent; // Suffixed with Event because of the override 26 | 27 | /// 28 | public SerializableInterfaceAdvancedDropdown( 29 | AdvancedDropdownState state, 30 | Type interfaceType, 31 | Scene? relevantScene, 32 | SerializedProperty property, 33 | bool classesOnly 34 | ) 35 | : base(state) 36 | { 37 | Assert.IsNotNull(interfaceType); 38 | 39 | sortChildrenMethod = typeof(AdvancedDropdownItem) 40 | .GetMethod("SortChildren", BindingFlags.Instance | BindingFlags.NonPublic); 41 | canSort = sortChildrenMethod != null; 42 | 43 | minimumSize = new Vector2(0, 300); 44 | this.interfaceType = interfaceType; 45 | this.relevantScene = relevantScene; 46 | this.property = property; 47 | this.classesOnly = classesOnly; 48 | } 49 | 50 | /// 51 | protected override AdvancedDropdownItem BuildRoot() 52 | { 53 | AdvancedDropdownItemWrapper item = new AdvancedDropdownItemWrapper(interfaceType.Name); 54 | 55 | item.AddChild(new ClassesItemBuilder(interfaceType).Build()); 56 | 57 | if (!classesOnly) 58 | { 59 | item.AddChild(new AssetsItemBuilder(interfaceType).Build()); 60 | item.AddChild(new SceneItemBuilder(interfaceType, relevantScene).Build()); 61 | } 62 | 63 | foreach (AdvancedDropdownItem dropdownItem in item.children) 64 | { 65 | dropdownItem.AddChild(new NoneDropdownItem()); 66 | } 67 | 68 | if (canSort) 69 | { 70 | sortChildrenMethod.Invoke(item, 71 | new object[] 72 | { 73 | (Comparison)Sort, true 74 | }); 75 | } 76 | 77 | return item; 78 | } 79 | 80 | private int Sort(AdvancedDropdownItem a, AdvancedDropdownItem b) 81 | { 82 | // For aesthetic reasons. Always puts the None first 83 | if (a is NoneDropdownItem) 84 | return -1; 85 | if (b is NoneDropdownItem) 86 | return 1; 87 | 88 | int childrenA = a.children.Count(); 89 | int childrenB = b.children.Count(); 90 | 91 | if (childrenA > 0 && childrenB > 0) 92 | return a.CompareTo(b); 93 | if (childrenA == 0 && childrenB == 0) 94 | return a.CompareTo(b); 95 | if (childrenA > 0 && childrenB == 0) 96 | return -1; 97 | return 1; 98 | } 99 | 100 | /// 101 | protected override void ItemSelected(AdvancedDropdownItem item) 102 | { 103 | if (item is IDropdownItem dropdownItem) 104 | { 105 | ItemSelectedEvent?.Invoke(property, dropdownItem.Mode, dropdownItem.GetValue()); 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Editor/Utilities/SerializableInterfaceAdvancedDropdown.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 440229d607e04089916ebda621c36108 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Utilities/SerializedPropertyExtensions.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | 3 | namespace TNRD.Utilities 4 | { 5 | internal static class SerializedPropertyExtensions 6 | { 7 | public static SerializedProperty ReferenceModeProperty(this SerializedProperty property) 8 | { 9 | return property.FindPropertyRelative("mode"); 10 | } 11 | 12 | public static SerializedProperty RawReferenceProperty(this SerializedProperty property) 13 | { 14 | return property.FindPropertyRelative("rawReference"); 15 | } 16 | 17 | public static SerializedProperty UnityReferenceProperty(this SerializedProperty property) 18 | { 19 | return property.FindPropertyRelative("unityReference"); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Editor/Utilities/SerializedPropertyExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8bfd876a31f9470b802c64d4f1a8dd37 3 | timeCreated: 1658567318 -------------------------------------------------------------------------------- /Editor/Utilities/SerializedPropertyUtilities.cs: -------------------------------------------------------------------------------- 1 | // Sourced from: https://github.com/dbrizov/NaughtyAttributes 2 | 3 | // MIT License 4 | // 5 | // Copyright (c) 2017 Denis Rizov 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | using System; 26 | using System.Collections; 27 | using System.Reflection; 28 | using UnityEditor; 29 | 30 | namespace TNRD.Utilities 31 | { 32 | public static class SerializedPropertyUtilities 33 | { 34 | /// 35 | /// Gets the value of a serialized property 36 | /// 37 | /// The property to get the value from 38 | public static object GetValue(SerializedProperty property) 39 | { 40 | string path = property.propertyPath.Replace(".Array.data[", "["); 41 | object targetObject = property.serializedObject.targetObject; 42 | string[] elements = path.Split('.'); 43 | 44 | for (int i = 0; i < elements.Length; i++) 45 | { 46 | string element = elements[i]; 47 | if (element.Contains("[")) 48 | { 49 | string elementName = element.Substring(0, element.IndexOf("[", StringComparison.OrdinalIgnoreCase)); 50 | int index = Convert.ToInt32(element 51 | .Substring(element.IndexOf("[", StringComparison.OrdinalIgnoreCase)) 52 | .Replace("[", string.Empty) 53 | .Replace("]", string.Empty)); 54 | targetObject = GetValue(targetObject, elementName, index); 55 | } 56 | else 57 | { 58 | targetObject = GetValue(targetObject, element); 59 | } 60 | } 61 | 62 | return targetObject; 63 | } 64 | 65 | private static object GetValue(object source, string name, int index) 66 | { 67 | IEnumerable enumerable = GetValue(source, name) as IEnumerable; 68 | if (enumerable == null) 69 | return null; 70 | 71 | IEnumerator enumerator = enumerable.GetEnumerator(); 72 | for (int i = 0; i <= index; i++) 73 | { 74 | if (!enumerator.MoveNext()) 75 | return null; 76 | } 77 | 78 | return enumerator.Current; 79 | } 80 | 81 | private static object GetValue(object source, string name) 82 | { 83 | if (source == null) 84 | return null; 85 | 86 | const BindingFlags fieldFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; 87 | const BindingFlags propertyFlags = fieldFlags | BindingFlags.IgnoreCase; 88 | 89 | Type type = source.GetType(); 90 | 91 | while (type != null) 92 | { 93 | FieldInfo field = type.GetField(name, fieldFlags); 94 | if (field != null) 95 | return field.GetValue(source); 96 | 97 | PropertyInfo property = type.GetProperty(name, propertyFlags); 98 | if (property != null) 99 | return property.GetValue(source, null); 100 | 101 | type = type.BaseType; 102 | } 103 | 104 | return null; 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Editor/Utilities/SerializedPropertyUtilities.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f7fcc0e7c5504e3bb618048c0dad4572 3 | timeCreated: 1662755656 -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Christiaan Bloemendaal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 75e1809be1e1aa44798912be71555951 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Serializable Interface 2 | 3 |

4 | GitHub package.json version 5 | 6 | GitHub issues 7 | 8 | 9 | GitHub pull requests 10 | 11 | 12 | GitHub license 13 | 14 | GitHub last commit 15 |

16 | 17 | A wrapper that allows you to serialize interfaces. Both UnityEngine.Object and regular object implementers work! 18 | 19 | ## Installation 20 | 1. The package is available on the [openupm registry](https://openupm.com). You can install it via [openupm-cli](https://github.com/openupm/openupm-cli). 21 | ``` 22 | openupm add net.tnrd.serializableinterface 23 | ``` 24 | 25 | 2. Installing through a [Unity Package](http://package-installer.glitch.me/v1/installer/package.openupm.com/net.tnrd.serializableinterface?registry=https://package.openupm.com) created by the [Package Installer Creator](https://package-installer.glitch.me) from [Needle](https://needle.tools) 26 | 27 | [](http://package-installer.glitch.me/v1/installer/package.openupm.com/net.tnrd.serializableinterface?registry=https://package.openupm.com) 28 | 29 | ## Usage 30 | 31 | Usage is pretty easy and straightforward. Assuming you have the following interface 32 | ```c# 33 | public interface IMyInterface 34 | { 35 | void Greet(); 36 | } 37 | ``` 38 | 39 | You can add it to a behaviour like so 40 | ```c# 41 | using TNRD; 42 | using UnityEngine; 43 | 44 | public class MyBehaviour : MonoBehaviour 45 | { 46 | [SerializeField] private SerializableInterface mySerializableInterface; 47 | 48 | private void Awake() 49 | { 50 | mySerializableInterface.Value.Greet(); 51 | } 52 | } 53 | ``` 54 | 55 | Back in the Unity inspector it will look like this 56 | 57 | ![image](https://user-images.githubusercontent.com/5531467/164994596-82ce84c8-27bc-4957-a297-f7c7d69c79d9.png) 58 | 59 | And when you click on the object selector button you will be shown a dropdown window with all possible options like this 60 | 61 | ![image](https://user-images.githubusercontent.com/5531467/164994604-15a0d060-72d1-440b-926b-883dd5f31955.png) 62 | 63 | As you can see you can select items from multiple locations: 64 | - Assets (Scriptable Objects and Prefabs that implement said interface) 65 | - Classes (custom classes that implement said interface) 66 | - Scene (objects in the scene that implement said interface) 67 | 68 | For the sake of example for the image above I have created some simple implementations 69 | 70 | ```c# 71 | using UnityEngine; 72 | 73 | public class MyComponent : MonoBehaviour, IMyInterface 74 | { 75 | /// 76 | public void Greet() 77 | { 78 | Debug.Log("Hello, World! I'm MyComponent"); 79 | } 80 | } 81 | ``` 82 | 83 | ```c# 84 | using UnityEngine; 85 | 86 | public class MyPoco : IMyInterface 87 | { 88 | /// 89 | public void Greet() 90 | { 91 | Debug.Log("Hello, World! I'm MyPoco"); 92 | } 93 | } 94 | ``` 95 | 96 | ```c# 97 | using UnityEngine; 98 | 99 | [CreateAssetMenu(menuName = "MyScriptable")] 100 | public class MyScriptable : ScriptableObject, IMyInterface 101 | { 102 | /// 103 | public void Greet() 104 | { 105 | Debug.Log("Hello, World! I'm MyScriptable"); 106 | } 107 | } 108 | ``` 109 | 110 | 111 | ## Support 112 | **Serializable Interface** is a small and open-source utility that I hope helps other people. It is by no means necessary but if you feel generous you can support me by donating. 113 | 114 | [![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/J3J11GEYY) 115 | 116 | ## Contributions 117 | Pull requests are welcomed. Please feel free to fix any issues you find, or add new features. 118 | 119 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ac9e779fcbb3c1e4f81f7c57ffad0237 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 76d3bf49cef9d564c99d96e38ba3c801 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("TNRD.SerializableInterface.Editor")] 4 | -------------------------------------------------------------------------------- /Runtime/AssemblyInfo.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bba409c4670e4b438882fb02c9cd6911 3 | timeCreated: 1650800163 -------------------------------------------------------------------------------- /Runtime/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using UnityEngine; 4 | 5 | namespace TNRD 6 | { 7 | public static class Extensions 8 | { 9 | /// 10 | /// Checks to see if the containing value is valid and returns it through the out parameter if so 11 | /// 12 | /// 13 | /// The containing value, if valid 14 | /// True if the containing value is valid, false if not 15 | public static bool IsDefined( 16 | this SerializableInterface serializableInterface, 17 | out TInterface value 18 | ) 19 | where TInterface : class 20 | { 21 | if (serializableInterface == null) 22 | { 23 | value = default; 24 | return false; 25 | } 26 | 27 | if (EqualityComparer.Default.Equals(serializableInterface.Value, default)) 28 | { 29 | value = default; 30 | return false; 31 | } 32 | 33 | value = serializableInterface.Value; 34 | return true; 35 | } 36 | 37 | /// 38 | public static bool TryGetValue( 39 | this SerializableInterface serializableInterface, 40 | out TInterface value 41 | ) 42 | where TInterface : class 43 | { 44 | return IsDefined(serializableInterface, out value); 45 | } 46 | 47 | /// 48 | /// Convert a IEnumerable of Interfaces to a List of SerializableInterfaces 49 | /// 50 | public static List> ToSerializableInterfaceList(this IEnumerable list) where T : class 51 | { 52 | return list.Select(e => new SerializableInterface(e)).ToList(); 53 | } 54 | 55 | /// 56 | /// Convert a IEnumerable of Interfaces to an Array of SerializableInterfaces 57 | /// 58 | public static SerializableInterface[] ToSerializableInterfaceArray(this IEnumerable list) where T : class 59 | { 60 | return list.Select(e => new SerializableInterface(e)).ToArray(); 61 | } 62 | 63 | public static TInterface Instantiate(this SerializableInterface serializableInterface) where TInterface : class 64 | { 65 | if (!serializableInterface.TryGetObject(out Object unityObject)) 66 | { 67 | throw new System.Exception($"Cannot instantiate {serializableInterface} because it's has no reference of type UnityEngine.Object"); 68 | } 69 | 70 | Object instantiatedObject = Object.Instantiate(unityObject); 71 | 72 | return GetInterfaceReference(instantiatedObject); 73 | } 74 | 75 | public static TInterface Instantiate(this SerializableInterface serializableInterface, Transform parent) where TInterface : class 76 | { 77 | if (!serializableInterface.TryGetObject(out Object unityObject)) 78 | { 79 | throw new System.Exception($"Cannot instantiate {serializableInterface} because it's has no reference of type UnityEngine.Object"); 80 | } 81 | 82 | Object instantiatedObject = Object.Instantiate(unityObject, parent); 83 | 84 | return GetInterfaceReference(instantiatedObject); 85 | } 86 | 87 | public static TInterface Instantiate(this SerializableInterface serializableInterface, Vector3 position, Quaternion rotation) where TInterface : class 88 | { 89 | if (!serializableInterface.TryGetObject(out Object unityObject)) 90 | { 91 | throw new System.Exception($"Cannot instantiate {serializableInterface} because it's has no reference of type UnityEngine.Object"); 92 | } 93 | 94 | Object instantiatedObject = Object.Instantiate(unityObject, position, rotation); 95 | 96 | return GetInterfaceReference(instantiatedObject); 97 | } 98 | 99 | public static TInterface Instantiate(this SerializableInterface serializableInterface, Vector3 position, Quaternion rotation, Transform parent) where TInterface : class 100 | { 101 | if (!serializableInterface.TryGetObject(out Object unityObject)) 102 | { 103 | throw new System.Exception($"Cannot instantiate {serializableInterface} because it's has no reference of type UnityEngine.Object"); 104 | } 105 | 106 | Object instantiatedObject = Object.Instantiate(unityObject, position, rotation, parent); 107 | 108 | return GetInterfaceReference(instantiatedObject); 109 | } 110 | 111 | private static TInterface GetInterfaceReference(Object instantiatedObject) where TInterface : class 112 | { 113 | if (instantiatedObject is GameObject gameObject) 114 | return gameObject.TryGetComponent(out TInterface component) ? component : null; 115 | 116 | return instantiatedObject as TInterface; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Runtime/Extensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 77acf133582b451fa1cea3bb60b48dc6 3 | timeCreated: 1659443598 -------------------------------------------------------------------------------- /Runtime/ISerializableInterface.cs: -------------------------------------------------------------------------------- 1 | namespace TNRD 2 | { 3 | internal interface ISerializableInterface 4 | { 5 | internal object GetRawReference(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Runtime/ISerializableInterface.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1e25825de5b944799b7209f3d1f061e4 3 | timeCreated: 1650975186 -------------------------------------------------------------------------------- /Runtime/ReferenceMode.cs: -------------------------------------------------------------------------------- 1 | namespace TNRD 2 | { 3 | internal enum ReferenceMode 4 | { 5 | Unity, 6 | Raw 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Runtime/ReferenceMode.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3f83fb44261d4082b2a93b1b064d7a8e 3 | timeCreated: 1650737480 -------------------------------------------------------------------------------- /Runtime/SerializableInterface.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | using UnityEngine; 4 | 5 | namespace TNRD 6 | { 7 | /// 8 | /// A wrapper around an interface that supports serialization for both UnityEngine.Object and regular object types 9 | /// 10 | /// The type of the interface you want to serialize 11 | [Serializable] 12 | public class SerializableInterface : ISerializableInterface where TInterface : class 13 | { 14 | [HideInInspector, SerializeField] private ReferenceMode mode = ReferenceMode.Unity; 15 | [HideInInspector, SerializeField] private UnityEngine.Object unityReference; 16 | [SerializeReference, UsedImplicitly] private object rawReference; 17 | 18 | public SerializableInterface() 19 | { 20 | } 21 | 22 | public SerializableInterface(TInterface value) 23 | { 24 | Value = value; 25 | } 26 | 27 | public TInterface Value 28 | { 29 | get 30 | { 31 | return mode switch 32 | { 33 | ReferenceMode.Raw => rawReference as TInterface, 34 | ReferenceMode.Unity => (object)unityReference as TInterface, 35 | _ => throw new ArgumentOutOfRangeException() 36 | }; 37 | } 38 | set 39 | { 40 | if (value is UnityEngine.Object unityObject) 41 | { 42 | rawReference = null; 43 | unityReference = unityObject; 44 | mode = ReferenceMode.Unity; 45 | } 46 | else 47 | { 48 | unityReference = null; 49 | rawReference = value; 50 | mode = ReferenceMode.Raw; 51 | } 52 | } 53 | } 54 | 55 | /// 56 | object ISerializableInterface.GetRawReference() 57 | { 58 | return rawReference; 59 | } 60 | 61 | public bool TryGetObject(out UnityEngine.Object unityObject) 62 | { 63 | unityObject = null; 64 | if (mode != ReferenceMode.Unity) return false; 65 | 66 | unityObject = unityReference; 67 | return true; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Runtime/SerializableInterface.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2f2ee52b6de249dfa199b767a2a777ac 3 | timeCreated: 1650737442 -------------------------------------------------------------------------------- /Runtime/TNRD.SerializableInterface.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TNRD.SerializableInterface", 3 | "rootNamespace": "TNRD", 4 | "references": [], 5 | "includePlatforms": [], 6 | "excludePlatforms": [], 7 | "allowUnsafeCode": false, 8 | "overrideReferences": false, 9 | "precompiledReferences": [], 10 | "autoReferenced": true, 11 | "defineConstraints": [], 12 | "versionDefines": [], 13 | "noEngineReferences": false 14 | } -------------------------------------------------------------------------------- /Runtime/TNRD.SerializableInterface.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 23eed6c2401dca1419d1ebd180e58c5a 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Samples~/SourceGeneration/SerializableInterfaceSourceGeneration.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thundernerd/Unity3D-SerializableInterface/71df240ccbb4f8fce73155336cf7c249d1c3bb44/Samples~/SourceGeneration/SerializableInterfaceSourceGeneration.dll -------------------------------------------------------------------------------- /Samples~/SourceGeneration/SerializableInterfaceSourceGeneration.dll.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4ac13508a171e574ca523db3ef4cde8c 3 | labels: 4 | - RoslynAnalyzer 5 | - RunOnlyOnAssembliesWithReference 6 | - SourceGenerator 7 | PluginImporter: 8 | externalObjects: {} 9 | serializedVersion: 2 10 | iconMap: {} 11 | executionOrder: {} 12 | defineConstraints: [] 13 | isPreloaded: 0 14 | isOverridable: 0 15 | isExplicitlyReferenced: 0 16 | validateReferences: 1 17 | platformData: 18 | - first: 19 | : Any 20 | second: 21 | enabled: 0 22 | settings: 23 | Exclude Editor: 1 24 | Exclude Linux64: 1 25 | Exclude OSXUniversal: 1 26 | Exclude Win: 1 27 | Exclude Win64: 1 28 | - first: 29 | Any: 30 | second: 31 | enabled: 0 32 | settings: {} 33 | - first: 34 | Editor: Editor 35 | second: 36 | enabled: 0 37 | settings: 38 | CPU: AnyCPU 39 | DefaultValueInitialized: true 40 | OS: AnyOS 41 | - first: 42 | Standalone: Linux64 43 | second: 44 | enabled: 0 45 | settings: 46 | CPU: None 47 | - first: 48 | Standalone: OSXUniversal 49 | second: 50 | enabled: 0 51 | settings: 52 | CPU: None 53 | - first: 54 | Standalone: Win 55 | second: 56 | enabled: 0 57 | settings: 58 | CPU: None 59 | - first: 60 | Standalone: Win64 61 | second: 62 | enabled: 0 63 | settings: 64 | CPU: None 65 | - first: 66 | Windows Store Apps: WindowsStoreApps 67 | second: 68 | enabled: 0 69 | settings: 70 | CPU: AnyCPU 71 | userData: 72 | assetBundleName: 73 | assetBundleVariant: 74 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "net.tnrd.serializableinterface", 3 | "version": "2.2.1", 4 | "displayName": "Serializable Interface", 5 | "unity": "2021.1", 6 | "description": "A wrapper that allows serialization of interfaces that supports both UnityEngine.Object and regular object types", 7 | "keywords": [ 8 | "serialize", 9 | "interface", 10 | "poco", 11 | "serialization" 12 | ], 13 | "author": { 14 | "name": "TNRD", 15 | "url": "https://www.tnrd.net" 16 | }, 17 | "samples": [ 18 | { 19 | "displayName": "Source Generation", 20 | "description": "Contains a dll that will allow you to use source generation", 21 | "path": "Samples~/SourceGeneration" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d5a8f3cf891464444b01862d463b5cde 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------