├── .gitattributes ├── .github └── workflows │ └── generate-docs.yml ├── .gitignore ├── .npmignore ├── .npmrc ├── CHANGELOG.md ├── CHANGELOG.md.meta ├── Documentation~ ├── articles │ ├── index.md │ ├── installation.md │ ├── letterboxing.md │ ├── menus.md │ └── screen-size.md ├── data │ ├── header.json │ └── sidenav.json └── images │ └── letterboxing.jpg ├── Editor.meta ├── Editor ├── UIGradientShaderGUI.cs ├── UIGradientShaderGUI.cs.meta ├── Zigurous.UI.Editor.asmdef └── Zigurous.UI.Editor.asmdef.meta ├── LICENSE.md ├── LICENSE.md.meta ├── Materials.meta ├── Materials ├── UIInverseMask-Content.mat ├── UIInverseMask-Content.mat.meta ├── UIInverseMask-Cutout.mat └── UIInverseMask-Cutout.mat.meta ├── README.md ├── README.md.meta ├── Runtime.meta ├── Runtime ├── CinematicBars.cs ├── CinematicBars.cs.meta ├── Extensions.meta ├── Extensions │ ├── CanvasExtensions.cs │ ├── CanvasExtensions.cs.meta │ ├── GraphicExtensions.cs │ ├── GraphicExtensions.cs.meta │ ├── RectTransformExtensions.cs │ └── RectTransformExtensions.cs.meta ├── InverseMask.cs ├── InverseMask.cs.meta ├── Letterboxing.cs ├── Letterboxing.cs.meta ├── NavigationStack.cs ├── NavigationStack.cs.meta ├── ScreenSizeListener.cs ├── ScreenSizeListener.cs.meta ├── ScrollDirection.cs ├── ScrollDirection.cs.meta ├── ScrollToSelection.cs ├── ScrollToSelection.cs.meta ├── ScrollWithInput.cs ├── ScrollWithInput.cs.meta ├── StretchToScreenSize.cs ├── StretchToScreenSize.cs.meta ├── Zigurous.UI.asmdef └── Zigurous.UI.asmdef.meta ├── Shaders.meta ├── Shaders ├── UIGradient.shader └── UIGradient.shader.meta ├── package.json └── package.json.meta /.gitattributes: -------------------------------------------------------------------------------- 1 | *.html linguist-detectable=false 2 | *.css linguist-detectable=false 3 | *.js linguist-detectable=false 4 | -------------------------------------------------------------------------------- /.github/workflows/generate-docs.yml: -------------------------------------------------------------------------------- 1 | name: Generate Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | generate: 15 | name: Docs 16 | uses: zigurous/docs/.github/workflows/unity-package.yml@main 17 | with: 18 | package_title: "UI Toolkit" 19 | package_base_path: com.zigurous.ui 20 | package_workflow: generate-docs.yml 21 | package_artifact: docs 22 | secrets: 23 | token: ${{ secrets.DOCS_TOKEN }} 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /**/obj/ 2 | /**/obj.meta 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | ./docs~/ 2 | .github/ 3 | .gitattributes 4 | .gitignore 5 | .gitmodules 6 | /**/obj/ 7 | /**/obj.meta 8 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org/ 2 | @zigurous:registry=https://npm.pkg.github.com 3 | //npm.pkg.github.com/:_authToken= 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.3.0] - 2022/11/02 9 | 10 | ### Added 11 | 12 | - UIGradient shader 13 | - Materials for creating inverse masks 14 | - `InverseMask` script to automatically apply inverse mask materials 15 | 16 | ### Changed 17 | 18 | - Renamed `Letterboxing` script to `CinematicBars` 19 | - Added new `Letterboxing` script that changes the camera viewport 20 | 21 | ## [0.2.0] - 2021/07/17 22 | 23 | ### Added 24 | 25 | - Letterboxing 26 | - ScreenSizeListener 27 | - StretchToScreenSize 28 | - CanvasExtensions 29 | - GraphicExtensions 30 | - RectTransformExtensions 31 | 32 | ## [0.1.0] - 2021/07/05 33 | 34 | ### Added 35 | 36 | - NavigationStack 37 | - ScrollToSelection 38 | - ScrollWithInput 39 | -------------------------------------------------------------------------------- /CHANGELOG.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f3a2dedc0a794cb4c80c7561bacaf129 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Documentation~/articles/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: "/manual" 3 | --- 4 | 5 | # UI Toolkit 6 | 7 | The **UI Toolkit** package contains scripts and utilities for creating UI in Unity projects. The package is intended to solve common problems that arise when developing UI and menus. The package is still early in development, and more functionality will be added over time. 8 | 9 |
10 | 11 | ## Overview 12 | 13 | #### ⚙️ [Installation](/installation) 14 | 15 | #### 🧰 [Scripting API](/api/Zigurous.UI) 16 | 17 | #### 📋 [Changelog](/changelog) 18 | 19 | #### ⚖️ [License](/license) 20 | 21 |
22 | 23 | ## Reference 24 | 25 | #### 📑 [Menu Tools](/manual/menus) 26 | 27 | #### 🎞️ [Letterboxing](/manual/letterboxing) 28 | 29 | #### 📏 [Screen Size](/manual/screen-size) 30 | -------------------------------------------------------------------------------- /Documentation~/articles/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: "/installation" 3 | --- 4 | 5 | # Installation 6 | 7 | Use the Unity [Package Manager](https://docs.unity3d.com/Manual/upm-ui.html) to install the **UI Toolkit** package. 8 | 9 | 1. Open the Package Manager in `Window > Package Manager` 10 | 2. Click the add (`+`) button in the status bar 11 | 3. Select `Add package from git URL` from the add menu 12 | 4. Enter the following Git URL in the text box and click Add: 13 | 14 | ```http 15 | https://github.com/zigurous/unity-ui-toolkit.git 16 | ``` 17 | 18 |
19 | 20 | ## 🏷️ Namespace 21 | 22 | Import the package namespace in each script or file you want to use it. You may need to regenerate project files/assemblies first. 23 | 24 | ```csharp 25 | using Zigurous.UI; 26 | ``` 27 | 28 |
29 | 30 | ## 💻 Source Code 31 | 32 | The source code for the **UI Toolkit** package is in the following repository: 33 | 34 | - https://github.com/zigurous/unity-ui-toolkit 35 | -------------------------------------------------------------------------------- /Documentation~/articles/letterboxing.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: "/manual/letterboxing" 3 | --- 4 | 5 | # Letterboxing 6 | 7 | Letterboxing is the process of changing an image or video's aspect ratio to another format while preserving the original aspect ratio. The resulting image has mattes (black bars) above and below it. The **UI Toolkit** packages includes a [Letterboxing](/api/Zigurous.UI/Letterboxing) script to handle this process. This script is most useful when displaying in-game cutscenes to give it a more cinematic feeling. 8 | 9 | ![](../images/letterboxing.jpg) 10 | 11 |
12 | 13 | ## 🎞️ Cinematic Bars 14 | 15 | Also included in the **UI Toolkit** package is the script [CinematicBars](/api/Zigurous.UI/CinematicBars) which allows you to simulate the letterboxing effect without actually changing the camera's viewport. This script displays the mattes (black bars) as UI elements overlayed in screen space. 16 | 17 | The color and material of the mattes can be customized as well as the desired aspect ratio. The script will animate the mattes in and out simply by enabling or disabling the script. 18 | -------------------------------------------------------------------------------- /Documentation~/articles/menus.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: "/manual/menus" 3 | --- 4 | 5 | # Menu Tools 6 | 7 | The **UI Toolkit** comes with a few scripts to assist in creating UI menus. There are several common problems that arise, especially in regards to menu navigation and controller-support. Generally speaking, basic navigation in menus across any input device can be achieved out-of-box using Unity's EventSystem component; however, this is not always the case. 8 | 9 |
10 | 11 | ### [NavigationStack](/api/Zigurous.UI/NavigationStack) 12 | 13 | This script manages a stack of game objects (menus) that you can navigate between. As you navigate to other menus or sub-menus, you push the menu's game object onto the stack. Then, when you need to navigate back to the previous menu, you simply pop the game object off the stack. The script can also automatically turn on/off the game objects as they are pushed and popped from the stack. The script also provides an InputAction to handle backwards navigation. 14 | 15 |
16 | 17 | ### [ScrollToSelection](/api/Zigurous.UI/ScrollToSelection) 18 | 19 | This script works in conjunction with the EventSystem to handle scrolling a ScrollRect component to the selected game object. For example, when using a controller you usually do not freely scroll menus that display a list of buttons, such as a level select menu. Rather, you simply navigate up and down to the next or previous button, respectively. The ScrollRect will automatically be scrolled to whichever game object is selected. 20 | 21 |
22 | 23 | ### [ScrollWithInput](/api/Zigurous.UI/ScrollWithInput) 24 | 25 | For other menus that use a ScrollRect component, you may just want to be able to freely scroll up and down. This may not be supported out of the box for controllers, so this script simply exposes an InputAction that you can bind controls to in order to scroll the menu. There is actually nothing specific to controllers in this script, so you can also use it with any kind of input, but generally mouse+keyboard already works with ScrollRect. 26 | -------------------------------------------------------------------------------- /Documentation~/articles/screen-size.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: "/manual/screen-size" 3 | --- 4 | 5 | # Screen Size 6 | 7 | Detecting changes to the screen size is very common for many different use cases in games. The **UI Toolkit** package includes the singleton [ScreenSizeListener](/api/Zigurous.UI/ScreenSizeListener) that allows you to register a callback to know when the screen size changes. 8 | 9 | ```csharp 10 | ScreenSizeListener.Instance.resized += OnResize; 11 | 12 | private void OnResize(float width, float height) 13 | { 14 | // handle resize here 15 | } 16 | ``` 17 | 18 | You can simply get the current screen size too. There seems to be a bug with Unity's `Screen.width` and `Screen.height` because they do not always return the correct values. Using the listener can be more reliable. 19 | 20 | ```csharp 21 | if (ScreenSizeListener.HasInstance) 22 | { 23 | // Individual properties 24 | float width = ScreenSizeListener.Instance.width; 25 | float height = ScreenSizeListener.Instance.height; 26 | 27 | // Single property 28 | Vector2Int size = ScreenSizeListener.Instance.size; 29 | } 30 | ``` 31 | -------------------------------------------------------------------------------- /Documentation~/data/header.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Manual", 4 | "path": "/manual" 5 | }, 6 | { 7 | "name": "Scripting API", 8 | "path": "/api" 9 | }, 10 | { 11 | "name": "Changelog", 12 | "path": "/changelog" 13 | }, 14 | { 15 | "name": "License", 16 | "path": "/license" 17 | } 18 | ] 19 | -------------------------------------------------------------------------------- /Documentation~/data/sidenav.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "📌 Overview", 4 | "items": [ 5 | { 6 | "name": "Getting Started", 7 | "path": "/manual" 8 | }, 9 | { 10 | "name": "Installation", 11 | "path": "/installation" 12 | }, 13 | { 14 | "name": "Changelog", 15 | "path": "/changelog" 16 | }, 17 | { 18 | "name": "License", 19 | "path": "/license" 20 | } 21 | ] 22 | }, 23 | { 24 | "title": "📖 Reference", 25 | "items": [ 26 | { 27 | "name": "Menu Tools", 28 | "path": "/manual/menus" 29 | }, 30 | { 31 | "name": "Letterboxing", 32 | "path": "/manual/letterboxing" 33 | }, 34 | { 35 | "name": "Screen Size", 36 | "path": "/manual/screen-size" 37 | } 38 | ] 39 | }, 40 | { 41 | "title": "💬 Contact", 42 | "items": [ 43 | { 44 | "name": "Discord", 45 | "href": "https://discord.gg/DdYyWVb", 46 | "icon": "launch" 47 | }, 48 | { 49 | "name": "Twitter", 50 | "href": "https://twitter.com/zigurous", 51 | "icon": "launch" 52 | } 53 | ] 54 | }, 55 | { 56 | "title": "🔗 Other Links", 57 | "items": [ 58 | { 59 | "name": "GitHub", 60 | "href": "https://github.com/zigurous/unity-ui-toolkit", 61 | "icon": "launch" 62 | }, 63 | { 64 | "name": "Asset Store", 65 | "href": "https://assetstore.unity.com/publishers/51884", 66 | "icon": "launch" 67 | }, 68 | { 69 | "name": "YouTube", 70 | "href": "https://youtube.com/c/zigurous?sub_confirmation=1", 71 | "icon": "launch" 72 | }, 73 | { 74 | "name": "Patreon", 75 | "href": "https://patreon.com/zigurous", 76 | "icon": "launch" 77 | } 78 | ] 79 | } 80 | ] 81 | -------------------------------------------------------------------------------- /Documentation~/images/letterboxing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zigurous/unity-ui-toolkit/1c4222df2cd61c737cb239ee3b565d19632f68f8/Documentation~/images/letterboxing.jpg -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d31a4d1c0db7c1040882d97cab645b4d 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/UIGradientShaderGUI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using UnityEditor; 4 | using UnityEngine; 5 | 6 | namespace Zigurous.UI.Editor 7 | { 8 | public sealed class UIGradientShaderGUI : ShaderGUI 9 | { 10 | public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties) 11 | { 12 | EditorGUILayout.Space(); 13 | 14 | Material material = materialEditor.target as Material; 15 | 16 | if (material == null) { 17 | throw new ArgumentNullException(nameof(material)); 18 | } 19 | 20 | GradientColorKey[] colorKeys = Enumerable 21 | .Range(0, material.GetInt("_Colors")) 22 | .Select((s, t) => new GradientColorKey(material.GetColor($"_Color{t}"), material.GetFloat($"_ColorTime{t}"))) 23 | .ToArray(); 24 | 25 | GradientAlphaKey[] alphaKeys = Enumerable 26 | .Range(0, material.GetInt("_Alphas")) 27 | .Select((s, t) => new GradientAlphaKey(material.GetFloat($"_Alpha{t}"), material.GetFloat($"_AlphaTime{t}"))) 28 | .ToArray(); 29 | 30 | Gradient gradient = new Gradient(); 31 | gradient.SetKeys(colorKeys, alphaKeys); 32 | 33 | EditorGUI.BeginChangeCheck(); 34 | 35 | Gradient field = EditorGUILayout.GradientField("Gradient", gradient); 36 | 37 | if (!EditorGUI.EndChangeCheck()) 38 | { 39 | base.OnGUI(materialEditor, properties); 40 | return; 41 | } 42 | 43 | for (int i = 0; i < 8; i++) 44 | { 45 | material.SetColor($"_Color{i}", Color.magenta); 46 | material.SetFloat($"_ColorTime{i}", -1f); 47 | material.SetFloat($"_Alpha{i}", -1f); 48 | material.SetFloat($"_AlphaTime{i}", -1f); 49 | } 50 | 51 | for (int i = 0; i < field.colorKeys.Length; i++) 52 | { 53 | material.SetColor($"_Color{i}", field.colorKeys[i].color); 54 | material.SetFloat($"_ColorTime{i}", field.colorKeys[i].time); 55 | } 56 | 57 | for (int i = 0; i < field.alphaKeys.Length; i++) 58 | { 59 | material.SetFloat($"_Alpha{i}", field.alphaKeys[i].alpha); 60 | material.SetFloat($"_AlphaTime{i}", field.alphaKeys[i].time); 61 | } 62 | 63 | material.SetInt("_Colors", field.colorKeys.Length); 64 | material.SetInt("_Alphas", field.alphaKeys.Length); 65 | 66 | EditorUtility.SetDirty(material); 67 | 68 | base.OnGUI(materialEditor, properties); 69 | } 70 | 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /Editor/UIGradientShaderGUI.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 70ee537964b955e46a01339a0d022953 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Zigurous.UI.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Zigurous.UI.Editor", 3 | "references": [ 4 | "Zigurous.UI" 5 | ], 6 | "includePlatforms": [ 7 | "Editor" 8 | ], 9 | "excludePlatforms": [], 10 | "allowUnsafeCode": false, 11 | "overrideReferences": false, 12 | "precompiledReferences": [], 13 | "autoReferenced": true, 14 | "defineConstraints": [], 15 | "versionDefines": [], 16 | "noEngineReferences": false 17 | } -------------------------------------------------------------------------------- /Editor/Zigurous.UI.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ff3e0322a7e9be44b925e33c5f242aed 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Zigurous 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: 450148839c6559b40b9e149cc67a954f 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Materials.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 339519c5250b1194c8092af7201caf1d 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Materials/UIInverseMask-Content.mat: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!21 &2100000 4 | Material: 5 | serializedVersion: 6 6 | m_ObjectHideFlags: 0 7 | m_CorrespondingSourceObject: {fileID: 0} 8 | m_PrefabInstance: {fileID: 0} 9 | m_PrefabAsset: {fileID: 0} 10 | m_Name: UIInverseMask-Content 11 | m_Shader: {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} 12 | m_ShaderKeywords: ETC1_EXTERNAL_ALPHA 13 | m_LightmapFlags: 4 14 | m_EnableInstancingVariants: 0 15 | m_DoubleSidedGI: 0 16 | m_CustomRenderQueue: -1 17 | stringTagMap: {} 18 | disabledShaderPasses: [] 19 | m_SavedProperties: 20 | serializedVersion: 3 21 | m_TexEnvs: 22 | - _BumpMap: 23 | m_Texture: {fileID: 0} 24 | m_Scale: {x: 1, y: 1} 25 | m_Offset: {x: 0, y: 0} 26 | - _DetailAlbedoMap: 27 | m_Texture: {fileID: 0} 28 | m_Scale: {x: 1, y: 1} 29 | m_Offset: {x: 0, y: 0} 30 | - _DetailMask: 31 | m_Texture: {fileID: 0} 32 | m_Scale: {x: 1, y: 1} 33 | m_Offset: {x: 0, y: 0} 34 | - _DetailNormalMap: 35 | m_Texture: {fileID: 0} 36 | m_Scale: {x: 1, y: 1} 37 | m_Offset: {x: 0, y: 0} 38 | - _EmissionMap: 39 | m_Texture: {fileID: 0} 40 | m_Scale: {x: 1, y: 1} 41 | m_Offset: {x: 0, y: 0} 42 | - _MainTex: 43 | m_Texture: {fileID: 0} 44 | m_Scale: {x: 1, y: 1} 45 | m_Offset: {x: 0, y: 0} 46 | - _MetallicGlossMap: 47 | m_Texture: {fileID: 0} 48 | m_Scale: {x: 1, y: 1} 49 | m_Offset: {x: 0, y: 0} 50 | - _OcclusionMap: 51 | m_Texture: {fileID: 0} 52 | m_Scale: {x: 1, y: 1} 53 | m_Offset: {x: 0, y: 0} 54 | - _ParallaxMap: 55 | m_Texture: {fileID: 0} 56 | m_Scale: {x: 1, y: 1} 57 | m_Offset: {x: 0, y: 0} 58 | m_Floats: 59 | - _BumpScale: 1 60 | - _ColorMask: 15 61 | - _Cutoff: 0.5 62 | - _DetailNormalMapScale: 1 63 | - _DstBlend: 0 64 | - _GlossMapScale: 1 65 | - _Glossiness: 0.5 66 | - _GlossyReflections: 1 67 | - _Metallic: 0 68 | - _Mode: 0 69 | - _OcclusionStrength: 1 70 | - _Parallax: 0.02 71 | - _SmoothnessTextureChannel: 0 72 | - _SpecularHighlights: 1 73 | - _SrcBlend: 1 74 | - _Stencil: 2 75 | - _StencilComp: 3 76 | - _StencilOp: 0 77 | - _StencilReadMask: 1 78 | - _StencilWriteMask: 0 79 | - _UVSec: 0 80 | - _UseUIAlphaClip: 0 81 | - _ZWrite: 1 82 | m_Colors: 83 | - _Color: {r: 1, g: 1, b: 1, a: 1} 84 | - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} 85 | -------------------------------------------------------------------------------- /Materials/UIInverseMask-Content.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 06f7a8df89e0df844858e626bf84b508 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 0 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Materials/UIInverseMask-Cutout.mat: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!21 &2100000 4 | Material: 5 | serializedVersion: 6 6 | m_ObjectHideFlags: 0 7 | m_CorrespondingSourceObject: {fileID: 0} 8 | m_PrefabInstance: {fileID: 0} 9 | m_PrefabAsset: {fileID: 0} 10 | m_Name: UIInverseMask-Cutout 11 | m_Shader: {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} 12 | m_ShaderKeywords: ETC1_EXTERNAL_ALPHA UNITY_UI_ALPHACLIP 13 | m_LightmapFlags: 4 14 | m_EnableInstancingVariants: 0 15 | m_DoubleSidedGI: 0 16 | m_CustomRenderQueue: -1 17 | stringTagMap: {} 18 | disabledShaderPasses: [] 19 | m_SavedProperties: 20 | serializedVersion: 3 21 | m_TexEnvs: 22 | - _BumpMap: 23 | m_Texture: {fileID: 0} 24 | m_Scale: {x: 1, y: 1} 25 | m_Offset: {x: 0, y: 0} 26 | - _DetailAlbedoMap: 27 | m_Texture: {fileID: 0} 28 | m_Scale: {x: 1, y: 1} 29 | m_Offset: {x: 0, y: 0} 30 | - _DetailMask: 31 | m_Texture: {fileID: 0} 32 | m_Scale: {x: 1, y: 1} 33 | m_Offset: {x: 0, y: 0} 34 | - _DetailNormalMap: 35 | m_Texture: {fileID: 0} 36 | m_Scale: {x: 1, y: 1} 37 | m_Offset: {x: 0, y: 0} 38 | - _EmissionMap: 39 | m_Texture: {fileID: 0} 40 | m_Scale: {x: 1, y: 1} 41 | m_Offset: {x: 0, y: 0} 42 | - _MainTex: 43 | m_Texture: {fileID: 0} 44 | m_Scale: {x: 1, y: 1} 45 | m_Offset: {x: 0, y: 0} 46 | - _MetallicGlossMap: 47 | m_Texture: {fileID: 0} 48 | m_Scale: {x: 1, y: 1} 49 | m_Offset: {x: 0, y: 0} 50 | - _OcclusionMap: 51 | m_Texture: {fileID: 0} 52 | m_Scale: {x: 1, y: 1} 53 | m_Offset: {x: 0, y: 0} 54 | - _ParallaxMap: 55 | m_Texture: {fileID: 0} 56 | m_Scale: {x: 1, y: 1} 57 | m_Offset: {x: 0, y: 0} 58 | m_Floats: 59 | - _BumpScale: 1 60 | - _ColorMask: 0 61 | - _Cutoff: 0.5 62 | - _DetailNormalMapScale: 1 63 | - _DstBlend: 0 64 | - _GlossMapScale: 1 65 | - _Glossiness: 0.5 66 | - _GlossyReflections: 1 67 | - _Metallic: 0 68 | - _Mode: 0 69 | - _OcclusionStrength: 1 70 | - _Parallax: 0.02 71 | - _SmoothnessTextureChannel: 0 72 | - _SpecularHighlights: 1 73 | - _SrcBlend: 1 74 | - _Stencil: 1 75 | - _StencilComp: 8 76 | - _StencilOp: 2 77 | - _StencilReadMask: 255 78 | - _StencilWriteMask: 255 79 | - _UVSec: 0 80 | - _UseUIAlphaClip: 1 81 | - _ZWrite: 1 82 | m_Colors: 83 | - _Color: {r: 1, g: 1, b: 1, a: 1} 84 | - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} 85 | -------------------------------------------------------------------------------- /Materials/UIInverseMask-Cutout.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: efee781990b78934a9de5ffb9cf227b5 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 0 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UI Toolkit 2 | 3 | [![](https://img.shields.io/badge/github-repo-blue?logo=github)](https://github.com/zigurous/unity-ui-toolkit) [![](https://img.shields.io/github/package-json/v/zigurous/unity-ui-toolkit)](https://github.com/zigurous/unity-ui-toolkit/releases) [![](https://img.shields.io/badge/docs-link-success)](https://docs.zigurous.com/com.zigurous.ui) [![](https://img.shields.io/github/license/zigurous/unity-ui-toolkit)](https://github.com/zigurous/unity-ui-toolkit/blob/main/LICENSE.md) 4 | 5 | The **UI Toolkit** package contains scripts and utilities for creating UI in Unity projects. The package is intended to solve common problems that arise when developing UI and menus. The package is still early in development, and more functionality will be added over time. 6 | 7 | ## Reference 8 | 9 | - [Menu Tools](https://docs.zigurous.com/com.zigurous.ui/manual/menus) 10 | - [Letterboxing](https://docs.zigurous.com/com.zigurous.ui/manual/letterboxing) 11 | - [Screen Size](https://docs.zigurous.com/com.zigurous.ui/manual/screen-size) 12 | 13 | ## Installation 14 | 15 | Use the Unity [Package Manager](https://docs.unity3d.com/Manual/upm-ui.html) to install the **UI Toolkit** package. 16 | 17 | 1. Open the Package Manager in `Window > Package Manager` 18 | 2. Click the add (`+`) button in the status bar 19 | 3. Select `Add package from git URL` from the add menu 20 | 4. Enter the following Git URL in the text box and click Add: 21 | 22 | ``` 23 | https://github.com/zigurous/unity-ui-toolkit.git 24 | ``` 25 | 26 | ## Namespace 27 | 28 | Import the package namespace in each script or file you want to use it. You may need to regenerate project files/assemblies first. 29 | 30 | ```csharp 31 | using Zigurous.UI; 32 | ``` 33 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c4d1ecb5625053e4b84282a4cc466f1d 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 96ade485c057e754ba4d03a59fd0c7ed 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/CinematicBars.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using UnityEngine; 3 | using UnityEngine.UI; 4 | 5 | namespace Zigurous.UI 6 | { 7 | /// 8 | /// Displays mattes on the top and bottom of the screen to crop the screen 9 | /// to a specified aspect ratio. This is also referred to as letterboxing 10 | /// and is useful for cutscenes. 11 | /// 12 | [RequireComponent(typeof(RectTransform))] 13 | [AddComponentMenu("Zigurous/UI/Misc/Cinematic Bars")] 14 | public sealed class CinematicBars : MonoBehaviour 15 | { 16 | [SerializeField] 17 | [Tooltip("The color of the mattes.")] 18 | private Color m_Color = Color.black; 19 | 20 | [SerializeField] 21 | [Tooltip("The material of the mattes.")] 22 | private Material m_Material = null; 23 | 24 | [SerializeField] 25 | [Tooltip("The aspect ratio of the mattes.")] 26 | private float m_AspectRatio = 2.35f; 27 | 28 | [SerializeField] 29 | [Tooltip("The amount of seconds it takes to animate the mattes.")] 30 | private float m_AnimationDuration = 0.5f; 31 | 32 | /// 33 | /// The top letterbox matte (Read only). 34 | /// 35 | public RectTransform matteTop { get; private set; } 36 | 37 | /// 38 | /// The bottom letterbox matte (Read only). 39 | /// 40 | public RectTransform matteBottom { get; private set; } 41 | 42 | /// 43 | /// The current height of the letterbox mattes (Read only). 44 | /// 45 | public float matteHeight { get; private set; } 46 | 47 | /// 48 | /// The color of the mattes. 49 | /// 50 | public Color color 51 | { 52 | get => m_Color; 53 | set { m_Color = value; UpdateStyles(); } 54 | } 55 | 56 | /// 57 | /// The material of the mattes. 58 | /// 59 | public Material material 60 | { 61 | get => m_Material; 62 | set { m_Material = value; UpdateStyles(); } 63 | } 64 | 65 | /// 66 | /// The aspect ratio of the mattes. 67 | /// 68 | public float aspectRatio 69 | { 70 | get => m_AspectRatio; 71 | set { m_AspectRatio = value; UpdateMattes(); } 72 | } 73 | 74 | /// 75 | /// The amount of seconds it takes to animate the mattes. 76 | /// 77 | public float animationDuration 78 | { 79 | get => m_AnimationDuration; 80 | set => m_AnimationDuration = value; 81 | } 82 | 83 | /// 84 | /// The coroutine that animates the mattes. 85 | /// 86 | private Coroutine coroutine; 87 | 88 | #if UNITY_EDITOR 89 | private bool invalidated; 90 | #endif 91 | 92 | private void Awake() 93 | { 94 | StretchToScreenSize stretch = gameObject.AddComponent(); 95 | stretch.hideFlags = HideFlags.HideInInspector; 96 | stretch.stretchWidth = true; 97 | stretch.stretchHeight = true; 98 | 99 | matteBottom = CreateMatte(); 100 | matteTop = CreateMatte(); 101 | matteTop.localScale = new Vector3(1f, -1f, 1f); 102 | 103 | SetDesiredHeight(0f); 104 | } 105 | 106 | private RectTransform CreateMatte() 107 | { 108 | GameObject matte = new GameObject("Matte"); 109 | matte.transform.parent = transform; 110 | 111 | RectTransform matteRect = matte.AddComponent(); 112 | matteRect.SetHeight(0f); 113 | 114 | matte.AddComponent(); 115 | 116 | Image graphic = matte.AddComponent(); 117 | graphic.color = color; 118 | graphic.material = material; 119 | 120 | StretchToScreenSize stretch = matte.AddComponent(); 121 | stretch.stretchWidth = true; 122 | stretch.stretchHeight = false; 123 | 124 | return matteRect; 125 | } 126 | 127 | private void Start() 128 | { 129 | ScreenSizeListener.Instance.resized += OnScreenResize; 130 | } 131 | 132 | private void OnDestroy() 133 | { 134 | if (ScreenSizeListener.HasInstance) { 135 | ScreenSizeListener.Instance.resized -= OnScreenResize; 136 | } 137 | } 138 | 139 | #if UNITY_EDITOR 140 | private void OnValidate() 141 | { 142 | invalidated = true; 143 | } 144 | #endif 145 | 146 | private void OnEnable() 147 | { 148 | UpdateMattes(); 149 | } 150 | 151 | private void OnDisable() 152 | { 153 | UpdateMattes(); 154 | } 155 | 156 | private void OnScreenResize(int width, int height) 157 | { 158 | UpdateMattes(animated: false); 159 | } 160 | 161 | #if UNITY_EDITOR 162 | private void Update() 163 | { 164 | if (invalidated) 165 | { 166 | UpdateStyles(); 167 | UpdateMattes(animated: false); 168 | 169 | invalidated = false; 170 | } 171 | } 172 | #endif 173 | 174 | private void UpdateMattes() 175 | { 176 | UpdateMattes(animated: animationDuration > 0f); 177 | } 178 | 179 | private void UpdateMattes(bool animated) 180 | { 181 | if (!gameObject.activeInHierarchy) 182 | { 183 | SetDesiredHeight(0f); 184 | return; 185 | } 186 | 187 | float desiredHeight = CalculateMatteHeight(); 188 | 189 | if (animated) 190 | { 191 | if (coroutine != null) { 192 | StopCoroutine(coroutine); 193 | } 194 | 195 | coroutine = StartCoroutine(Animate(matteHeight, desiredHeight)); 196 | } 197 | else 198 | { 199 | SetDesiredHeight(desiredHeight); 200 | } 201 | } 202 | 203 | private IEnumerator Animate(float currentHeight, float desiredHeight) 204 | { 205 | float elapsed = 0f; 206 | 207 | while (elapsed < animationDuration) 208 | { 209 | float percent = Mathf.Clamp01(elapsed / animationDuration); 210 | float height = Mathf.SmoothStep(currentHeight, desiredHeight, percent); 211 | 212 | SetDesiredHeight(height); 213 | 214 | elapsed += Time.deltaTime; 215 | yield return null; 216 | } 217 | 218 | SetDesiredHeight(desiredHeight); 219 | 220 | coroutine = null; 221 | } 222 | 223 | private float CalculateMatteHeight() 224 | { 225 | if (!enabled) { 226 | return 0f; 227 | } 228 | 229 | float screenWidth = Screen.width; 230 | float screenHeight = Screen.height; 231 | 232 | // Screen.width and Screen.height oddly does not always give the 233 | // correct values so try to use ScreenSizeListener if available 234 | if (ScreenSizeListener.HasInstance) 235 | { 236 | screenWidth = ScreenSizeListener.Instance.width; 237 | screenHeight = ScreenSizeListener.Instance.height; 238 | } 239 | 240 | float letterbox = screenWidth / aspectRatio; 241 | float height = (screenHeight - letterbox) / 2f; 242 | 243 | return float.IsNaN(height) ? 0f : height; 244 | } 245 | 246 | private void SetDesiredHeight(float height) 247 | { 248 | matteHeight = height; 249 | 250 | if (matteTop != null) { 251 | matteTop.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 0f, height); 252 | } 253 | 254 | if (matteBottom != null) { 255 | matteBottom.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Bottom, 0f, height); 256 | } 257 | } 258 | 259 | private void UpdateStyles() 260 | { 261 | if (matteTop != null) 262 | { 263 | Graphic graphic = matteTop.GetComponent(); 264 | graphic.color = color; 265 | graphic.material = material; 266 | } 267 | 268 | if (matteBottom != null) 269 | { 270 | Graphic graphic = matteBottom.GetComponent(); 271 | graphic.color = color; 272 | graphic.material = material; 273 | } 274 | } 275 | 276 | } 277 | 278 | } 279 | -------------------------------------------------------------------------------- /Runtime/CinematicBars.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: edd698e7de211f94190a6d2108bd18c1 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Extensions.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5ed78365befa494498137b3b97bdd0f6 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Extensions/CanvasExtensions.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.UI; 3 | 4 | namespace Zigurous.UI 5 | { 6 | /// 7 | /// Extension methods for UI canvas. 8 | /// 9 | public static class CanvasExtensions 10 | { 11 | /// 12 | /// Fades the alpha of all graphics of the canvas to 100% over the given 13 | /// duration. 14 | /// 15 | /// The canvas to fade. 16 | /// The amount of seconds it takes to fade the graphics. 17 | /// Ignores the time scale when fading the graphics. 18 | public static void FadeInGraphics(this Canvas canvas, float duration, bool ignoreTimeScale = false) 19 | { 20 | Graphic[] graphics = canvas.GetComponentsInChildren(); 21 | 22 | for (int i = 0; i < graphics.Length; i++) { 23 | graphics[i].CrossFadeAlpha(1f, duration, ignoreTimeScale); 24 | } 25 | } 26 | 27 | /// 28 | /// Fades the alpha of all graphics of the canvas to 0% over the given 29 | /// duration. 30 | /// 31 | /// The canvas to fade. 32 | /// The amount of seconds it takes to fade the graphics. 33 | /// Ignores the time scale when fading the graphics. 34 | public static void FadeOutGraphics(this Canvas canvas, float duration, bool ignoreTimeScale = false) 35 | { 36 | Graphic[] graphics = canvas.GetComponentsInChildren(); 37 | 38 | for (int i = 0; i < graphics.Length; i++) { 39 | graphics[i].CrossFadeAlpha(0f, duration, ignoreTimeScale); 40 | } 41 | } 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /Runtime/Extensions/CanvasExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f24822dad860ee64fbaaa32076539d38 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Extensions/GraphicExtensions.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine.UI; 2 | 3 | namespace Zigurous.UI 4 | { 5 | /// 6 | /// Extension methods for UI graphics. 7 | /// 8 | public static class GraphicExtensions 9 | { 10 | /// 11 | /// Fades the alpha of the graphic to 100% over the given duration. 12 | /// 13 | /// The graphic to fade. 14 | /// The amount of seconds it takes to fade the graphic. 15 | /// Ignores the time scale when fading the graphic. 16 | public static void FadeIn(this Graphic graphic, float duration, bool ignoreTimeScale = false) 17 | { 18 | graphic.CrossFadeAlpha(1f, duration, ignoreTimeScale); 19 | } 20 | 21 | /// 22 | /// Fades the alpha of the graphic to 0% over the given duration. 23 | /// 24 | /// The graphic to fade. 25 | /// The amount of seconds it takes to fade the graphic. 26 | /// Ignores the time scale when fading the graphic. 27 | public static void FadeOut(this Graphic graphic, float duration, bool ignoreTimeScale = false) 28 | { 29 | graphic.CrossFadeAlpha(0f, duration, ignoreTimeScale); 30 | } 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Runtime/Extensions/GraphicExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3fbff746fbe88e74183bc62297d544fd 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Extensions/RectTransformExtensions.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Zigurous.UI 4 | { 5 | /// 6 | /// Extension methods for RectTransform. 7 | /// 8 | public static class RectTransformExtensions 9 | { 10 | private static Vector3[] corners = new Vector3[4]; 11 | 12 | /// 13 | /// Calculates the world-space rect of the transform. 14 | /// 15 | /// The transform to calculate the world rect of. 16 | /// Ignores the scale of the transform when setting the rect size (default=false). 17 | /// The world rect of the transform. 18 | public static Rect GetWorldRect(this RectTransform transform, bool ignoreScale = false) 19 | { 20 | transform.GetWorldCorners(corners); 21 | 22 | Vector3 topLeft = corners[0]; 23 | Vector3 size = transform.rect.size; 24 | 25 | if (!ignoreScale) { 26 | size.Scale(transform.lossyScale); 27 | } 28 | 29 | return new Rect(topLeft, size); 30 | } 31 | 32 | /// 33 | /// Sets the width of the rect transform to the given value. 34 | /// 35 | /// The rect transform to update. 36 | /// The width value to set. 37 | public static void SetWidth(this RectTransform rect, float width) => 38 | rect.sizeDelta = new Vector2(width, rect.sizeDelta.y); 39 | 40 | /// 41 | /// Sets the height of the rect transform to the given value. 42 | /// 43 | /// The rect transform to update. 44 | /// The height value to set. 45 | public static void SetHeight(this RectTransform rect, float height) => 46 | rect.sizeDelta = new Vector2(rect.sizeDelta.x, height); 47 | 48 | /// 49 | /// Sets the left offset of the rect transform to the given value. 50 | /// 51 | /// The rect transform to update. 52 | /// The left offset value to set. 53 | public static void SetLeft(this RectTransform rect, float left) => 54 | rect.offsetMin = new Vector2(left, rect.offsetMin.y); 55 | 56 | /// 57 | /// Sets the right offset of the rect transform to the given value. 58 | /// 59 | /// The rect transform to update. 60 | /// The right offset value to set. 61 | public static void SetRight(this RectTransform rect, float right) => 62 | rect.offsetMax = new Vector2(-right, rect.offsetMax.y); 63 | 64 | /// 65 | /// Sets the top offset of the rect transform to the given value. 66 | /// 67 | /// The rect transform to update. 68 | /// The top offset value to set. 69 | public static void SetTop(this RectTransform rect, float top) => 70 | rect.offsetMax = new Vector2(rect.offsetMax.x, -top); 71 | 72 | /// 73 | /// Sets the bottom offset of the rect transform to the given value. 74 | /// 75 | /// The rect transform to update. 76 | /// The bottom offset value to set. 77 | public static void SetBottom(this RectTransform rect, float bottom) => 78 | rect.offsetMin = new Vector2(rect.offsetMin.x, bottom); 79 | 80 | /// 81 | /// Sets the left anchor of the rect transform to the given value. 82 | /// 83 | /// The rect transform to update. 84 | /// The left anchor value to set. 85 | public static void SetAnchorMinX(this RectTransform rect, float minX) => 86 | rect.anchorMin = new Vector2(minX, rect.anchorMin.y); 87 | 88 | /// 89 | /// Sets the bottom anchor of the rect transform to the given value. 90 | /// 91 | /// The rect transform to update. 92 | /// The bottom anchor value to set. 93 | public static void SetAnchorMinY(this RectTransform rect, float minY) => 94 | rect.anchorMin = new Vector2(rect.anchorMin.x, minY); 95 | 96 | /// 97 | /// Sets the right anchor of the rect transform to the given value. 98 | /// 99 | /// The rect transform to update. 100 | /// The right anchor value to set. 101 | public static void SetAnchorMaxX(this RectTransform rect, float maxX) => 102 | rect.anchorMax = new Vector2(maxX, rect.anchorMax.y); 103 | 104 | /// 105 | /// Sets the top anchor of the rect transform to the given value. 106 | /// 107 | /// The rect transform to update. 108 | /// The top anchor value to set. 109 | public static void SetAnchorMaxY(this RectTransform rect, float maxY) => 110 | rect.anchorMax = new Vector2(rect.anchorMax.x, maxY); 111 | 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /Runtime/Extensions/RectTransformExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 548c76697311bea478208a1bf002e25f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/InverseMask.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.UI; 3 | 4 | namespace Zigurous.UI 5 | { 6 | /// 7 | /// Applies materials to the image and its children to create an 8 | /// inverse mask. 9 | /// 10 | [ExecuteAlways] 11 | [RequireComponent(typeof(Image))] 12 | [AddComponentMenu("Zigurous/UI/Effects/Inverse Mask")] 13 | public sealed class InverseMask : MonoBehaviour 14 | { 15 | private static Material m_CutoutMaterial; 16 | private static Material m_ContentMaterial; 17 | 18 | /// 19 | /// The shared material applied to an image that is masking another 20 | /// image. This image acts as a cutout, thus its contents are not visible. 21 | /// 22 | public static Material cutoutMaterial 23 | { 24 | get 25 | { 26 | if (m_CutoutMaterial == null) 27 | { 28 | m_CutoutMaterial = new Material(Shader.Find("UI/Default")); 29 | m_CutoutMaterial.name = "UIInverseMask-Cutout"; 30 | m_CutoutMaterial.color = new Color(1f, 1f, 1f, 1f / 256f); 31 | m_CutoutMaterial.shaderKeywords = new string[] { "UNITY_UI_ALPHACLIP" }; 32 | m_CutoutMaterial.SetInt("_Stencil", 1); 33 | m_CutoutMaterial.SetInt("_StencilComp", 8); 34 | m_CutoutMaterial.SetInt("_StencilOp", 2); 35 | m_CutoutMaterial.SetInt("_StencilWriteMask", 255); 36 | m_CutoutMaterial.SetInt("_StencilReadMask", 255); 37 | m_CutoutMaterial.SetInt("_ColorMask", 0); 38 | m_CutoutMaterial.SetInt("_UseUIAlphaClip", 1); 39 | } 40 | 41 | return m_CutoutMaterial; 42 | } 43 | } 44 | 45 | /// 46 | /// The shared material applied to the children images being masked. 47 | /// The content of these images will be visible in the shape of the 48 | /// parent cutout mask. 49 | /// 50 | public static Material contentMaterial 51 | { 52 | get 53 | { 54 | if (m_ContentMaterial == null) 55 | { 56 | m_ContentMaterial = new Material(Shader.Find("UI/Default")); 57 | m_ContentMaterial.name = "UIInverseMask-Content"; 58 | m_ContentMaterial.color = Color.white; 59 | m_ContentMaterial.SetInt("_Stencil", 2); 60 | m_ContentMaterial.SetInt("_StencilComp", 3); 61 | m_ContentMaterial.SetInt("_StencilOp", 0); 62 | m_ContentMaterial.SetInt("_StencilWriteMask", 0); 63 | m_ContentMaterial.SetInt("_StencilReadMask", 1); 64 | m_ContentMaterial.SetInt("_ColorMask", 15); 65 | m_ContentMaterial.SetInt("_UseUIAlphaClip", 0); 66 | } 67 | 68 | return m_ContentMaterial; 69 | } 70 | } 71 | 72 | #if UNITY_EDITOR 73 | private bool invalidated; 74 | 75 | private void OnValidate() 76 | { 77 | invalidated = true; 78 | } 79 | 80 | private void Update() 81 | { 82 | if (invalidated) 83 | { 84 | Apply(); 85 | invalidated = false; 86 | } 87 | } 88 | #endif 89 | 90 | private void OnEnable() 91 | { 92 | Apply(); 93 | } 94 | 95 | private void OnDisable() 96 | { 97 | Remove(); 98 | } 99 | 100 | private void Apply() 101 | { 102 | Image mask = GetComponent(); 103 | mask.material = InverseMask.cutoutMaterial; 104 | 105 | Image[] images = GetComponentsInChildren(); 106 | for (int i = 0; i < images.Length; i++) 107 | { 108 | Image image = images[i]; 109 | 110 | if (image.transform != transform) { 111 | image.material = InverseMask.contentMaterial; 112 | } 113 | } 114 | } 115 | 116 | private void Remove() 117 | { 118 | Image mask = GetComponent(); 119 | mask.material = null; 120 | 121 | Image[] images = GetComponentsInChildren(); 122 | for (int i = 0; i < images.Length; i++) 123 | { 124 | Image image = images[i]; 125 | 126 | if (image.transform != transform) { 127 | image.material = null; 128 | } 129 | } 130 | } 131 | 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /Runtime/InverseMask.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4788d26ccf600544c9843b994e5c2bd5 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Letterboxing.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using UnityEngine; 3 | 4 | namespace Zigurous.UI 5 | { 6 | /// 7 | /// Crops the screen to a specified aspect ratio by changing the camera's 8 | /// viewport. This is known as letterboxing and is useful for cutscenes. 9 | /// 10 | [RequireComponent(typeof(Camera))] 11 | [AddComponentMenu("Zigurous/UI/Misc/Letterboxing")] 12 | public sealed class Letterboxing : MonoBehaviour 13 | { 14 | [SerializeField] 15 | [Tooltip("The aspect ratio of the mattes.")] 16 | private float m_AspectRatio = 2.35f; 17 | 18 | [SerializeField] 19 | [Tooltip("The amount of seconds it takes to animate the mattes.")] 20 | private float m_AnimationDuration = 0.5f; 21 | 22 | /// 23 | /// The camera being letterboxed (Read only). 24 | /// 25 | public new Camera camera { get; private set; } 26 | 27 | /// 28 | /// The current height of the viewport (Read only). 29 | /// 30 | public float viewportHeight => camera.rect.height; 31 | 32 | /// 33 | /// The aspect ratio of the mattes. 34 | /// 35 | public float aspectRatio 36 | { 37 | get => m_AspectRatio; 38 | set { m_AspectRatio = value; UpdateViewport(); } 39 | } 40 | 41 | /// 42 | /// The amount of seconds it takes to animate the mattes. 43 | /// 44 | public float animationDuration 45 | { 46 | get => m_AnimationDuration; 47 | set => m_AnimationDuration = value; 48 | } 49 | 50 | /// 51 | /// The coroutine that animates the mattes. 52 | /// 53 | private Coroutine coroutine; 54 | 55 | #if UNITY_EDITOR 56 | private bool invalidated; 57 | #endif 58 | 59 | private void Awake() 60 | { 61 | camera = GetComponent(); 62 | } 63 | 64 | private void Start() 65 | { 66 | ScreenSizeListener.Instance.resized += OnScreenResize; 67 | } 68 | 69 | private void OnDestroy() 70 | { 71 | if (ScreenSizeListener.HasInstance) { 72 | ScreenSizeListener.Instance.resized -= OnScreenResize; 73 | } 74 | } 75 | 76 | #if UNITY_EDITOR 77 | private void OnValidate() 78 | { 79 | invalidated = true; 80 | } 81 | #endif 82 | 83 | private void OnEnable() 84 | { 85 | UpdateViewport(); 86 | } 87 | 88 | private void OnDisable() 89 | { 90 | UpdateViewport(); 91 | } 92 | 93 | private void OnScreenResize(int width, int height) 94 | { 95 | UpdateViewport(animated: false); 96 | } 97 | 98 | #if UNITY_EDITOR 99 | private void Update() 100 | { 101 | if (invalidated) 102 | { 103 | UpdateViewport(animated: false); 104 | 105 | invalidated = false; 106 | } 107 | } 108 | #endif 109 | 110 | private void UpdateViewport() 111 | { 112 | UpdateViewport(animated: animationDuration > 0f); 113 | } 114 | 115 | private void UpdateViewport(bool animated) 116 | { 117 | if (!gameObject.activeInHierarchy) 118 | { 119 | SetViewportHeight(1f); 120 | return; 121 | } 122 | 123 | float desiredHeight = CalculateViewportHeight(); 124 | 125 | if (animated) 126 | { 127 | if (coroutine != null) { 128 | StopCoroutine(coroutine); 129 | } 130 | 131 | coroutine = StartCoroutine(Animate(viewportHeight, desiredHeight)); 132 | } 133 | else 134 | { 135 | SetViewportHeight(desiredHeight); 136 | } 137 | } 138 | 139 | private IEnumerator Animate(float currentHeight, float desiredHeight) 140 | { 141 | float elapsed = 0f; 142 | 143 | while (elapsed < animationDuration) 144 | { 145 | float percent = Mathf.Clamp01(elapsed / animationDuration); 146 | float height = Mathf.SmoothStep(currentHeight, desiredHeight, percent); 147 | 148 | SetViewportHeight(height); 149 | 150 | elapsed += Time.deltaTime; 151 | yield return null; 152 | } 153 | 154 | SetViewportHeight(desiredHeight); 155 | 156 | coroutine = null; 157 | } 158 | 159 | private float CalculateViewportHeight() 160 | { 161 | if (!enabled) { 162 | return 1f; 163 | } 164 | 165 | float screenWidth = Screen.width; 166 | float screenHeight = Screen.height; 167 | 168 | // Screen.width and Screen.height oddly does not always give the 169 | // correct values so try to use ScreenSizeListener if available 170 | if (ScreenSizeListener.HasInstance) 171 | { 172 | screenWidth = ScreenSizeListener.Instance.width; 173 | screenHeight = ScreenSizeListener.Instance.height; 174 | } 175 | 176 | float letterbox = screenWidth / aspectRatio; 177 | float height = (screenHeight - letterbox) / 2f; 178 | float viewport = 1f - ((height / screenHeight) * 2f); 179 | 180 | return float.IsNaN(viewport) ? 1f : viewport; 181 | } 182 | 183 | private void SetViewportHeight(float height) 184 | { 185 | Rect rect = camera.rect; 186 | rect.height = height; 187 | rect.y = (1f - height) / 2f; 188 | 189 | camera.rect = rect; 190 | } 191 | 192 | } 193 | 194 | } 195 | -------------------------------------------------------------------------------- /Runtime/Letterboxing.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 843d186670a1d034ab44841facabc66f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/NavigationStack.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | using UnityEngine.EventSystems; 4 | #if ENABLE_INPUT_SYSTEM 5 | using UnityEngine.InputSystem; 6 | #endif 7 | 8 | namespace Zigurous.UI 9 | { 10 | /// 11 | /// Manages a stack of game objects for menu navigation purposes. This is 12 | /// especially useful to handle backwards navigation by simply popping off 13 | /// the last item in the stack. 14 | /// 15 | [RequireComponent(typeof(EventSystem))] 16 | [AddComponentMenu("Zigurous/UI/Navigation/Navigation Stack")] 17 | public class NavigationStack : MonoBehaviour 18 | { 19 | /// 20 | /// The event system being tracked by the navigation stack (Read only). 21 | /// 22 | public EventSystem eventSystem { get; private set; } 23 | 24 | /// 25 | /// The game objects added to the stack (Read only). 26 | /// 27 | public Stack items { get; private set; } 28 | 29 | /// 30 | /// The current selected game object at the top of the stack (Read only). 31 | /// 32 | public GameObject top => items.Count > 0 ? items.Peek() : null; 33 | 34 | #if ENABLE_INPUT_SYSTEM 35 | /// 36 | /// The input action that handles backwards navigation by popping items 37 | /// off the stack. 38 | /// 39 | [Tooltip("The input action that handles backwards navigation by popping items off the stack.")] 40 | public InputAction backNavigationInput = new InputAction("MenuBackNavigation", InputActionType.Button); 41 | #endif 42 | 43 | #if ENABLE_LEGACY_INPUT_MANAGER 44 | /// 45 | /// The input button that handles backwards navigation by popping items 46 | /// off the stack. 47 | /// 48 | [Tooltip("The input button that handles backwards navigation by popping items off the stack.")] 49 | public string legacyBackNavigationInput = "Cancel"; 50 | #endif 51 | 52 | /// 53 | /// The root game object added to the bottom of the stack. 54 | /// 55 | [Tooltip("The root game object added to the bottom of the stack.")] 56 | public GameObject rootGameObject; 57 | 58 | /// 59 | /// Automatically sets the active state of game objects as they are 60 | /// pushed on and off the stack. 61 | /// 62 | [Tooltip("Automatically sets the active state of game objects as they are pushed on and off the stack.")] 63 | public bool setActiveState = true; 64 | 65 | /// 66 | /// Allows for all items to be popped off the stack. Often times you 67 | /// want to maintain at least the root game object. 68 | /// 69 | [Tooltip("Allows for all items to be popped off the stack. Often times you want to maintain at least the root game object.")] 70 | public bool allowEmptyStack = false; 71 | 72 | /// 73 | /// Allows for null game objects to be pushed onto the stack. 74 | /// 75 | [Tooltip("Allows for null game objects to be pushed onto the stack.")] 76 | public bool allowNullSelections = false; 77 | 78 | #if ENABLE_INPUT_SYSTEM 79 | private void Reset() 80 | { 81 | backNavigationInput = new InputAction("MenuBackNavigation", InputActionType.Button); 82 | backNavigationInput.AddBinding("/escape"); 83 | backNavigationInput.AddBinding("/backspace"); 84 | backNavigationInput.AddBinding("/select"); 85 | backNavigationInput.AddBinding("/buttonEast"); 86 | } 87 | #endif 88 | 89 | private void Awake() 90 | { 91 | items = new Stack(8); 92 | eventSystem = GetComponent(); 93 | 94 | #if ENABLE_INPUT_SYSTEM 95 | backNavigationInput.performed += OnBack; 96 | #endif 97 | } 98 | 99 | private void Start() 100 | { 101 | Push(rootGameObject); 102 | } 103 | 104 | #if ENABLE_INPUT_SYSTEM 105 | private void OnEnable() 106 | { 107 | backNavigationInput.Enable(); 108 | } 109 | 110 | private void OnDisable() 111 | { 112 | backNavigationInput.Disable(); 113 | } 114 | #endif 115 | 116 | #if ENABLE_LEGACY_INPUT_MANAGER 117 | private void Update() 118 | { 119 | try 120 | { 121 | if (!string.IsNullOrEmpty(legacyBackNavigationInput) && Input.GetButtonDown(legacyBackNavigationInput)) { 122 | Pop(); 123 | } 124 | } 125 | catch 126 | { 127 | #if UNITY_EDITOR || DEVELOPMENT_BUILD 128 | Debug.LogWarning($"[NavigationStack] Input button '{legacyBackNavigationInput}' is not setup.\nDefine the input in the Input Manager settings accessed from the menu: Edit > Project Settings"); 129 | #endif 130 | } 131 | } 132 | #endif 133 | 134 | /// 135 | /// Pushes a game object onto the stack, effectively navigating 136 | /// forwards. 137 | /// 138 | /// The game object to push onto the stack. 139 | public void Push(GameObject selected) 140 | { 141 | if (selected != null || allowNullSelections) 142 | { 143 | if (setActiveState && selected != null) { 144 | selected.SetActive(true); 145 | } 146 | 147 | eventSystem.SetSelectedGameObject(selected); 148 | items.Push(selected); 149 | } 150 | } 151 | 152 | /// 153 | /// Pops the last game object off the stack, effectively navigating 154 | /// backwards. 155 | /// 156 | /// The game object that was popped off the stack. 157 | public GameObject Pop() 158 | { 159 | if (items.Count == 0) { 160 | return null; 161 | } 162 | 163 | GameObject top = null; 164 | 165 | // Pop off the top of the stack 166 | if (items.Count > 1 || allowEmptyStack) 167 | { 168 | top = items.Pop(); 169 | 170 | if (setActiveState && top != null) { 171 | top.SetActive(false); 172 | } 173 | 174 | eventSystem.SetSelectedGameObject(null); 175 | } 176 | 177 | // Set the previous item to be active 178 | if (items.Count > 0) 179 | { 180 | GameObject previous = items.Peek(); 181 | 182 | if (setActiveState && previous != null) { 183 | previous.SetActive(true); 184 | } 185 | 186 | eventSystem.SetSelectedGameObject(previous); 187 | } 188 | 189 | return top; 190 | } 191 | 192 | #if ENABLE_INPUT_SYSTEM 193 | private void OnBack(InputAction.CallbackContext context) 194 | { 195 | if (context.performed) { 196 | Pop(); 197 | } 198 | } 199 | #endif 200 | 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /Runtime/NavigationStack.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6f6342acb6c2cc54eab471d9e3b94c60 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/ScreenSizeListener.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Zigurous.UI 4 | { 5 | /// 6 | /// Listens for changes in the screen size. 7 | /// 8 | [AddComponentMenu("")] 9 | public sealed class ScreenSizeListener : MonoBehaviour 10 | { 11 | private static volatile ScreenSizeListener instance; 12 | private static object lockObject = new object(); 13 | private static bool isUnloading = false; 14 | 15 | /// 16 | /// The current instance of the class. The instance will be created if 17 | /// it does not already exist. 18 | /// 19 | /// The instance of the class. 20 | public static ScreenSizeListener Instance 21 | { 22 | get 23 | { 24 | if (isUnloading) { 25 | return null; 26 | } 27 | 28 | if (instance == null) 29 | { 30 | lock (lockObject) 31 | { 32 | instance = FindObjectOfType(); 33 | 34 | if (instance == null) 35 | { 36 | GameObject singleton = new GameObject(); 37 | singleton.name = typeof(ScreenSizeListener).Name; 38 | singleton.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector; 39 | singleton.AddComponent(); 40 | DontDestroyOnLoad(singleton); 41 | } 42 | } 43 | } 44 | 45 | return instance; 46 | } 47 | } 48 | 49 | /// 50 | /// Checks if the singleton has been initialized and an instance is 51 | /// available to use. 52 | /// 53 | /// True if an instance is available, false otherwise. 54 | public static bool HasInstance => instance != null; 55 | 56 | /// 57 | /// A function delegate invoked when the screen size changes. 58 | /// 59 | /// The new width of the screen. 60 | /// The new height of the screen. 61 | public delegate void OnResize(int width, int height); 62 | 63 | /// 64 | /// A callback invoked when the screen size changes. 65 | /// 66 | public OnResize resized; 67 | 68 | /// 69 | /// The current size of the screen (Read only). 70 | /// 71 | public Vector2Int size => new Vector2Int(width, height); 72 | 73 | /// 74 | /// The current width of the screen (Read only). 75 | /// 76 | public int width { get; private set; } 77 | 78 | /// 79 | /// The current height of the screen (Read only). 80 | /// 81 | public int height { get; private set; } 82 | 83 | private ScreenSizeListener() {} 84 | 85 | private void Awake() 86 | { 87 | isUnloading = false; 88 | 89 | if (instance == null) 90 | { 91 | instance = this; 92 | 93 | width = Screen.width; 94 | height = Screen.height; 95 | } 96 | else { 97 | Destroy(this); 98 | } 99 | } 100 | 101 | private void OnDestroy() 102 | { 103 | resized = null; 104 | 105 | isUnloading = true; 106 | 107 | if (instance == this) { 108 | instance = null; 109 | } 110 | } 111 | 112 | private void Update() 113 | { 114 | if (Screen.width != width || Screen.height != height) 115 | { 116 | width = Screen.width; 117 | height = Screen.height; 118 | 119 | if (resized != null) { 120 | resized.Invoke(width, height); 121 | } 122 | } 123 | } 124 | 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /Runtime/ScreenSizeListener.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3fa4a3d96d00bb74ebaa8ec68db725d8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/ScrollDirection.cs: -------------------------------------------------------------------------------- 1 | namespace Zigurous.UI 2 | { 3 | /// 4 | /// A scroll direction. 5 | /// 6 | public enum ScrollDirection 7 | { 8 | /// 9 | /// Scrolls in the y-axis. 10 | /// 11 | Vertical, 12 | 13 | /// 14 | /// Scrolls in the x-axis. 15 | /// 16 | Horizontal, 17 | 18 | /// 19 | /// Scrolls in both the x-axis and the y-axis. 20 | /// 21 | Both, 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Runtime/ScrollDirection.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1c998d00f8614bd4c9d425e829eece23 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/ScrollToSelection.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.EventSystems; 3 | #if ENABLE_INPUT_SYSTEM 4 | using UnityEngine.InputSystem; 5 | #endif 6 | using UnityEngine.UI; 7 | 8 | namespace Zigurous.UI 9 | { 10 | /// 11 | /// Handles scrolling a ScrollRect component to the selected child element. 12 | /// This is especially useful for controller support. 13 | /// 14 | [RequireComponent(typeof(ScrollRect))] 15 | [AddComponentMenu("Zigurous/UI/Navigation/Scroll To Selection")] 16 | public class ScrollToSelection : MonoBehaviour 17 | { 18 | /// 19 | /// The ScrollRect component being scrolled (Read only). 20 | /// 21 | public ScrollRect scrollRect { get; private set; } 22 | 23 | /// 24 | /// The RectTransform component of the scroll rect (Read only). 25 | /// 26 | public RectTransform scrollTransform { get; private set; } 27 | 28 | /// 29 | /// The current selected game object (Read only). 30 | /// 31 | public GameObject selectedGameObject { get; private set; } 32 | 33 | /// 34 | /// The RectTransform of the current selected game object (Read only). 35 | /// 36 | public RectTransform selectedTransform { get; private set; } 37 | 38 | /// 39 | /// The direction to scroll the ScrollRect. 40 | /// 41 | [Tooltip("The direction to scroll the ScrollRect.")] 42 | public ScrollDirection scrollDirection = ScrollDirection.Vertical; 43 | 44 | /// 45 | /// How quickly the ScrollRect scrolls. 46 | /// 47 | [Tooltip("How quickly the ScrollRect scrolls.")] 48 | public float scrollSpeed = 10f; 49 | 50 | /// 51 | /// Whether the ScrollRect is currently being scrolled manually. This 52 | /// allows the user to freely scroll with the mouse even when a child 53 | /// element is selected (Read only). 54 | /// 55 | public bool manualScrolling { get; private set; } 56 | 57 | private void Awake() 58 | { 59 | scrollRect = GetComponent(); 60 | scrollTransform = scrollRect.GetComponent(); 61 | } 62 | 63 | private void Update() 64 | { 65 | CheckForManualScrolling(); 66 | SetSelectedGameObject(); 67 | SetScrollPosition(); 68 | } 69 | 70 | private void CheckForManualScrolling() 71 | { 72 | #if ENABLE_INPUT_SYSTEM 73 | if (Mouse.current != null) 74 | { 75 | switch (scrollDirection) 76 | { 77 | case ScrollDirection.Horizontal: 78 | if (Mouse.current.scroll.x.IsActuated()) { 79 | manualScrolling = true; 80 | } 81 | break; 82 | 83 | case ScrollDirection.Vertical: 84 | if (Mouse.current.scroll.y.IsActuated()) { 85 | manualScrolling = true; 86 | } 87 | break; 88 | 89 | case ScrollDirection.Both: 90 | if (Mouse.current.scroll.x.IsActuated() || Mouse.current.scroll.y.IsActuated()) { 91 | manualScrolling = true; 92 | } 93 | break; 94 | } 95 | } 96 | #elif ENABLE_LEGACY_INPUT_MANAGER 97 | switch (scrollDirection) 98 | { 99 | case ScrollDirection.Horizontal: 100 | if (Input.mouseScrollDelta.x != 0f) { 101 | manualScrolling = true; 102 | } 103 | break; 104 | 105 | case ScrollDirection.Vertical: 106 | if (Input.mouseScrollDelta.y != 0f) { 107 | manualScrolling = true; 108 | } 109 | break; 110 | 111 | case ScrollDirection.Both: 112 | if (Input.mouseScrollDelta.x != 0f || Input.mouseScrollDelta.y != 0f) { 113 | manualScrolling = true; 114 | } 115 | break; 116 | } 117 | #endif 118 | } 119 | 120 | private void SetSelectedGameObject() 121 | { 122 | EventSystem eventSystem = EventSystem.current; 123 | 124 | if (eventSystem == null || eventSystem.currentSelectedGameObject == null) { 125 | return; 126 | } 127 | 128 | if (eventSystem.currentSelectedGameObject != selectedGameObject && 129 | eventSystem.currentSelectedGameObject.transform.parent == scrollRect.content) 130 | { 131 | selectedGameObject = eventSystem.currentSelectedGameObject; 132 | selectedTransform = selectedGameObject.GetComponent(); 133 | manualScrolling = false; 134 | } 135 | } 136 | 137 | private void SetScrollPosition() 138 | { 139 | if (selectedTransform == null || manualScrolling) { 140 | return; 141 | } 142 | 143 | switch (scrollDirection) 144 | { 145 | case ScrollDirection.Vertical: 146 | ScrollVertical(selectedTransform); 147 | break; 148 | 149 | case ScrollDirection.Horizontal: 150 | ScrollHorizontal(selectedTransform); 151 | break; 152 | 153 | case ScrollDirection.Both: 154 | ScrollVertical(selectedTransform); 155 | ScrollHorizontal(selectedTransform); 156 | break; 157 | } 158 | } 159 | 160 | private void ScrollVertical(RectTransform selection) 161 | { 162 | // Calculate the scroll offset 163 | float elementHeight = selection.rect.height; 164 | float maskHeight = scrollTransform.rect.height; 165 | float anchorPosition = scrollRect.content.anchoredPosition.y; 166 | float selectionPosition = -selection.anchoredPosition.y - (elementHeight * (1f - selection.pivot.y)); 167 | float offset = GetScrollOffset(selectionPosition, anchorPosition, elementHeight, maskHeight); 168 | 169 | // Move the target scroll rect 170 | float position = scrollRect.verticalNormalizedPosition; 171 | position = Mathf.Clamp01(position + (offset / scrollRect.content.rect.height) * Time.unscaledDeltaTime * scrollSpeed); 172 | scrollRect.verticalNormalizedPosition = position; 173 | } 174 | 175 | private void ScrollHorizontal(RectTransform selection) 176 | { 177 | // Calculate the scroll offset 178 | float selectionPosition = -selection.anchoredPosition.x - (selection.rect.width * (1f - selection.pivot.x)); 179 | float elementWidth = selection.rect.width; 180 | float maskWidth = scrollTransform.rect.width; 181 | float anchorPosition = -scrollRect.content.anchoredPosition.x; 182 | float offset = -GetScrollOffset(selectionPosition, anchorPosition, elementWidth, maskWidth); 183 | 184 | // Move the target scroll rect 185 | float position = scrollRect.horizontalNormalizedPosition; 186 | position = Mathf.Clamp01(position + (offset / scrollRect.content.rect.width) * Time.unscaledDeltaTime * scrollSpeed); 187 | scrollRect.horizontalNormalizedPosition = position; 188 | } 189 | 190 | private float GetScrollOffset(float position, float anchorPosition, float targetLength, float maskLength) 191 | { 192 | if (position < anchorPosition + (targetLength * 0.5f)) { 193 | return (anchorPosition + maskLength) - (position - targetLength); 194 | } 195 | else if (position + targetLength > anchorPosition + maskLength) { 196 | return (anchorPosition + maskLength) - (position + targetLength); 197 | } 198 | 199 | return 0f; 200 | } 201 | 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /Runtime/ScrollToSelection.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6ad76fd4d532d9e40b56ed26dcd17656 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/ScrollWithInput.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.EventSystems; 3 | #if ENABLE_INPUT_SYSTEM 4 | using UnityEngine.InputSystem; 5 | #endif 6 | using UnityEngine.UI; 7 | 8 | namespace Zigurous.UI 9 | { 10 | /// 11 | /// Handles scrolling a ScrollRect component with user input. This is 12 | /// especially useful for controller support. 13 | /// 14 | [RequireComponent(typeof(ScrollRect))] 15 | [AddComponentMenu("Zigurous/UI/Navigation/Scroll With Input")] 16 | public class ScrollWithInput : MonoBehaviour 17 | { 18 | /// 19 | /// The ScrollRect component being scrolled (Read only). 20 | /// 21 | public ScrollRect scrollRect { get; private set; } 22 | 23 | #if ENABLE_INPUT_SYSTEM 24 | /// 25 | /// The input action that handles scrolling. 26 | /// 27 | [Tooltip("The input action that handles scrolling.")] 28 | public InputAction scrollInput = new InputAction("ScrollInput", InputActionType.Value, null, null, null, "Vector2"); 29 | #endif 30 | 31 | #if ENABLE_LEGACY_INPUT_MANAGER 32 | /// 33 | /// The legacy input axis that handles scrolling in the y-axis. 34 | /// 35 | [Tooltip("The legacy input axis that handles scrolling in the y-axis.")] 36 | public string legacyScrollInputY = ""; 37 | 38 | /// 39 | /// The legacy input axis that handles scrolling in the x-axis. 40 | /// 41 | [Tooltip("The legacy input axis that handles scrolling in the x-axis.")] 42 | public string legacyScrollInputX = ""; 43 | #endif 44 | 45 | /// 46 | /// The sensitivity multiplier applied to the input. 47 | /// 48 | [Tooltip("The sensitivity multiplier applied to the input.")] 49 | public float sensitivity = 1f; 50 | 51 | /// 52 | /// The direction to scroll the ScrollRect. 53 | /// 54 | [Tooltip("The direction to scroll the ScrollRect.")] 55 | public ScrollDirection direction = ScrollDirection.Vertical; 56 | 57 | private void Awake() 58 | { 59 | scrollRect = GetComponent(); 60 | } 61 | 62 | #if ENABLE_INPUT_SYSTEM 63 | private void Reset() 64 | { 65 | scrollInput = new InputAction("ScrollInput", InputActionType.Value, null, null, null, "Vector2"); 66 | scrollInput.AddBinding("/rightStick"); 67 | scrollInput.AddBinding("/leftStick"); 68 | scrollInput.AddBinding("/dpad"); 69 | } 70 | 71 | private void OnEnable() 72 | { 73 | scrollInput.Enable(); 74 | } 75 | 76 | private void OnDisable() 77 | { 78 | scrollInput.Disable(); 79 | } 80 | #endif 81 | 82 | private void Update() 83 | { 84 | EventSystem eventSystem = EventSystem.current; 85 | 86 | if (eventSystem == null || eventSystem.currentSelectedGameObject == null) { 87 | return; 88 | } 89 | 90 | if (eventSystem.currentSelectedGameObject == scrollRect.gameObject || 91 | eventSystem.currentSelectedGameObject.transform.parent == scrollRect.content) 92 | { 93 | Vector2 input = Vector2.zero; 94 | 95 | #if ENABLE_INPUT_SYSTEM 96 | input = scrollInput.ReadValue(); 97 | #endif 98 | 99 | #if ENABLE_LEGACY_INPUT_MANAGER 100 | if (input.y == 0f) { 101 | input.y = GetAxis(legacyScrollInputX); 102 | } 103 | 104 | if (input.x == 0f) { 105 | input.x = GetAxis(legacyScrollInputY); 106 | } 107 | #endif 108 | 109 | switch (direction) 110 | { 111 | case ScrollDirection.Vertical: 112 | input.x = 0f; 113 | break; 114 | 115 | case ScrollDirection.Horizontal: 116 | input.y = 0f; 117 | break; 118 | } 119 | 120 | scrollRect.normalizedPosition += input * sensitivity * Time.unscaledDeltaTime; 121 | } 122 | } 123 | 124 | #if ENABLE_LEGACY_INPUT_MANAGER 125 | private float GetAxis(string inputName) 126 | { 127 | float value = 0f; 128 | 129 | try 130 | { 131 | if (!string.IsNullOrEmpty(inputName)) { 132 | value = Input.GetAxis(inputName); 133 | } 134 | } 135 | catch 136 | { 137 | #if UNITY_EDITOR || DEVELOPMENT_BUILD 138 | Debug.LogWarning($"[ScrollWithInput] Input axis '{inputName}' is not setup.\nDefine the input in the Input Manager settings accessed from the menu: Edit > Project Settings"); 139 | #endif 140 | } 141 | 142 | return value; 143 | } 144 | #endif 145 | 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /Runtime/ScrollWithInput.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b086e248ab0756c43ab350e4170c7a29 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/StretchToScreenSize.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Zigurous.UI 4 | { 5 | /// 6 | /// Stretches a UI element to the screen size. 7 | /// 8 | [RequireComponent(typeof(RectTransform))] 9 | [AddComponentMenu("Zigurous/UI/Misc/Stretch to Screen Size")] 10 | public sealed class StretchToScreenSize : MonoBehaviour 11 | { 12 | /// 13 | /// The RectTransform component of the object. 14 | /// 15 | public RectTransform rectTransform { get; private set; } 16 | 17 | /// 18 | /// Stretches the width of the RectTransform to match the screen width. 19 | /// 20 | [Tooltip("Stretches the width of the RectTransform to match the screen width.")] 21 | public bool stretchWidth = true; 22 | 23 | /// 24 | /// Stretches the height of the RectTransform to match the screen height. 25 | /// 26 | [Tooltip("Stretches the height of the RectTransform to match the screen height.")] 27 | public bool stretchHeight = true; 28 | 29 | private void Awake() 30 | { 31 | rectTransform = GetComponent(); 32 | } 33 | 34 | private void Start() 35 | { 36 | ScreenSizeListener.Instance.resized += OnScreenResize; 37 | } 38 | 39 | private void OnDestroy() 40 | { 41 | if (ScreenSizeListener.HasInstance) { 42 | ScreenSizeListener.Instance.resized -= OnScreenResize; 43 | } 44 | } 45 | 46 | private void OnEnable() 47 | { 48 | Stretch(); 49 | } 50 | 51 | private void OnScreenResize(int width, int height) 52 | { 53 | if (enabled) { 54 | Stretch(); 55 | } 56 | } 57 | 58 | /// 59 | /// Stretches the RectTransform to match the screen size. 60 | /// 61 | public void Stretch() 62 | { 63 | Stretch(stretchWidth, stretchHeight); 64 | } 65 | 66 | /// 67 | /// Stretches the RectTransform to match the screen size. 68 | /// 69 | /// Whether to stretch the width. 70 | /// Whether to stretch the height. 71 | public void Stretch(bool stretchWidth, bool stretchHeight) 72 | { 73 | if (stretchWidth) 74 | { 75 | rectTransform.SetAnchorMinX(0f); 76 | rectTransform.SetAnchorMaxX(1f); 77 | rectTransform.SetLeft(0f); 78 | rectTransform.SetRight(0f); 79 | } 80 | 81 | if (stretchHeight) 82 | { 83 | rectTransform.SetAnchorMinY(0f); 84 | rectTransform.SetAnchorMaxY(1f); 85 | rectTransform.SetBottom(0f); 86 | rectTransform.SetTop(0f); 87 | } 88 | } 89 | 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /Runtime/StretchToScreenSize.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6c834a245d125c94facf6505110f99ac 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Zigurous.UI.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Zigurous.UI", 3 | "references": [ 4 | "GUID:75469ad4d38634e559750d17036d5f7c" 5 | ], 6 | "includePlatforms": [], 7 | "excludePlatforms": [], 8 | "allowUnsafeCode": false, 9 | "overrideReferences": false, 10 | "precompiledReferences": [], 11 | "autoReferenced": true, 12 | "defineConstraints": [], 13 | "versionDefines": [], 14 | "noEngineReferences": false 15 | } -------------------------------------------------------------------------------- /Runtime/Zigurous.UI.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d519592eaeb82894890523c42c9dbfe5 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Shaders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 946c01f521bbf804dbeb46949b571f20 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Shaders/UIGradient.shader: -------------------------------------------------------------------------------- 1 | Shader "Zigurous/UI/Gradient" 2 | { 3 | Properties 4 | { 5 | [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} 6 | [HideInInspector] _Color ("Tint", Color) = (1,1,1,1) 7 | 8 | [HideInInspector] _StencilComp ("Stencil Comparison", Float) = 8 9 | [HideInInspector] _Stencil ("Stencil ID", Float) = 0 10 | [HideInInspector] _StencilOp ("Stencil Operation", Float) = 0 11 | [HideInInspector] _StencilWriteMask ("Stencil Write Mask", Float) = 255 12 | [HideInInspector] _StencilReadMask ("Stencil Read Mask", Float) = 255 13 | 14 | [HideInInspector] _ColorMask ("Color Mask", Float) = 15 15 | 16 | _Rotation ("Rotation", Range(0, 180)) = 0 17 | 18 | [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0 19 | 20 | [HideInInspector] _Color0 ("Color 0", Color) = (1, 1, 1, 1) 21 | [HideInInspector] _Color1 ("Color 1", Color) = (1, 1, 1, 1) 22 | [HideInInspector] _Color2 ("Color 2", Color) = (1, 1, 1, 1) 23 | [HideInInspector] _Color3 ("Color 3", Color) = (1, 1, 1, 1) 24 | [HideInInspector] _Color4 ("Color 4", Color) = (1, 1, 1, 1) 25 | [HideInInspector] _Color5 ("Color 5", Color) = (1, 1, 1, 1) 26 | [HideInInspector] _Color6 ("Color 6", Color) = (1, 1, 1, 1) 27 | [HideInInspector] _Color7 ("Color 7", Color) = (1, 1, 1, 1) 28 | 29 | [HideInInspector] _ColorTime0 ("Color Time 0", Float) = 0 30 | [HideInInspector] _ColorTime1 ("Color Time 1", Float) = 0 31 | [HideInInspector] _ColorTime2 ("Color Time 2", Float) = 0 32 | [HideInInspector] _ColorTime3 ("Color Time 3", Float) = 0 33 | [HideInInspector] _ColorTime4 ("Color Time 4", Float) = 0 34 | [HideInInspector] _ColorTime5 ("Color Time 5", Float) = 0 35 | [HideInInspector] _ColorTime6 ("Color Time 6", Float) = 0 36 | [HideInInspector] _ColorTime7 ("Color Time 7", Float) = 0 37 | 38 | [HideInInspector] _Colors ("Colors", Int) = 0 39 | 40 | [HideInInspector] _Alpha0 ("Alpha 0", Float) = 0 41 | [HideInInspector] _Alpha1 ("Alpha 1", Float) = 0 42 | [HideInInspector] _Alpha2 ("Alpha 2", Float) = 0 43 | [HideInInspector] _Alpha3 ("Alpha 3", Float) = 0 44 | [HideInInspector] _Alpha4 ("Alpha 4", Float) = 0 45 | [HideInInspector] _Alpha5 ("Alpha 5", Float) = 0 46 | [HideInInspector] _Alpha6 ("Alpha 6", Float) = 0 47 | [HideInInspector] _Alpha7 ("Alpha 7", Float) = 0 48 | 49 | [HideInInspector] _AlphaTime0 ("Alpha Time 0", Float) = 0 50 | [HideInInspector] _AlphaTime1 ("Alpha Time 1", Float) = 0 51 | [HideInInspector] _AlphaTime2 ("Alpha Time 2", Float) = 0 52 | [HideInInspector] _AlphaTime3 ("Alpha Time 3", Float) = 0 53 | [HideInInspector] _AlphaTime4 ("Alpha Time 4", Float) = 0 54 | [HideInInspector] _AlphaTime5 ("Alpha Time 5", Float) = 0 55 | [HideInInspector] _AlphaTime6 ("Alpha Time 6", Float) = 0 56 | [HideInInspector] _AlphaTime7 ("Alpha Time 7", Float) = 0 57 | 58 | [HideInInspector] _Alphas ("Alphas", Int) = 0 59 | } 60 | 61 | SubShader 62 | { 63 | Tags 64 | { 65 | "Queue"="Transparent" 66 | "IgnoreProjector"="True" 67 | "RenderType"="Transparent" 68 | "PreviewType"="Plane" 69 | "CanUseSpriteAtlas"="True" 70 | } 71 | 72 | Stencil 73 | { 74 | Ref [_Stencil] 75 | Comp [_StencilComp] 76 | Pass [_StencilOp] 77 | ReadMask [_StencilReadMask] 78 | WriteMask [_StencilWriteMask] 79 | } 80 | 81 | Cull Off 82 | Lighting Off 83 | ZWrite Off 84 | ZTest [unity_GUIZTestMode] 85 | Blend SrcAlpha OneMinusSrcAlpha 86 | ColorMask [_ColorMask] 87 | 88 | Pass 89 | { 90 | Name "Default" 91 | 92 | CGPROGRAM 93 | 94 | #pragma vertex vert 95 | #pragma fragment frag 96 | #pragma target 2.0 97 | 98 | #include "UnityCG.cginc" 99 | #include "UnityUI.cginc" 100 | 101 | #pragma multi_compile_local _ UNITY_UI_CLIP_RECT 102 | #pragma multi_compile_local _ UNITY_UI_ALPHACLIP 103 | 104 | struct appdata_t 105 | { 106 | float4 vertex : POSITION; 107 | float4 color : COLOR; 108 | float2 texcoord : TEXCOORD0; 109 | 110 | UNITY_VERTEX_INPUT_INSTANCE_ID 111 | }; 112 | 113 | struct v2f 114 | { 115 | float4 vertex : SV_POSITION; 116 | fixed4 color : COLOR; 117 | float2 texcoord : TEXCOORD0; 118 | float4 worldPosition : TEXCOORD1; 119 | 120 | UNITY_VERTEX_OUTPUT_STEREO 121 | }; 122 | 123 | sampler2D _MainTex; 124 | fixed4 _Color; 125 | fixed4 _TextureSampleAdd; 126 | float4 _ClipRect; 127 | float4 _MainTex_ST; 128 | float _Rotation; 129 | 130 | float4 _Color0, _Color1, _Color2, _Color3, _Color4, _Color5, _Color6, _Color7; 131 | float _Alpha0, _Alpha1, _Alpha2, _Alpha3, _Alpha4, _Alpha5, _Alpha6, _Alpha7; 132 | float _ColorTime0, _ColorTime1, _ColorTime2, _ColorTime3, _ColorTime4, _ColorTime5, _ColorTime6, _ColorTime7; 133 | float _AlphaTime0, _AlphaTime1, _AlphaTime2, _AlphaTime3, _AlphaTime4, _AlphaTime5, _AlphaTime6, _AlphaTime7; 134 | int _Colors, _Alphas; 135 | 136 | float4 get_gradient (float2 texcoord) 137 | { 138 | float4 cc[8] = { _Color0, _Color1, _Color2, _Color3, _Color4, _Color5, _Color6, _Color7 }; 139 | float aa[8] = { _Alpha0, _Alpha1, _Alpha2, _Alpha3, _Alpha4, _Alpha5, _Alpha6, _Alpha7 }; 140 | float ct[8] = { _ColorTime0, _ColorTime1, _ColorTime2, _ColorTime3, _ColorTime4, _ColorTime5, _ColorTime6, _ColorTime7 }; 141 | float at[8] = { _AlphaTime0, _AlphaTime1, _AlphaTime2, _AlphaTime3, _AlphaTime4, _AlphaTime5, _AlphaTime6, _AlphaTime7 }; 142 | 143 | float4 cv1 = cc[0], cv2 = cc[_Colors - 1]; 144 | float ct1 = ct[0], ct2 = ct[_Colors - 1]; 145 | float av1 = aa[0], av2 = aa[_Alphas - 1]; 146 | float at1 = at[0], at2 = at[_Alphas - 1]; 147 | 148 | float t = texcoord.x; 149 | 150 | for (int i = 0; i < _Colors; i++) 151 | { 152 | if (ct[i] > t) 153 | break; 154 | 155 | cv1 = cc[i]; 156 | ct1 = ct[i]; 157 | } 158 | 159 | for (int j = 0; j < _Colors; j++) 160 | { 161 | if (ct[j] < t) 162 | continue; 163 | 164 | cv2 = cc[j]; 165 | ct2 = ct[j]; 166 | break; 167 | } 168 | 169 | for (int k = 0; k < _Alphas; k++) 170 | { 171 | if (at[k] > t) 172 | break; 173 | 174 | av1 = aa[k]; 175 | at1 = at[k]; 176 | } 177 | 178 | for (int l = 0; l < _Alphas; l++) 179 | { 180 | if (at[l] < t) 181 | continue; 182 | 183 | av2 = aa[l]; 184 | at2 = at[l]; 185 | break; 186 | } 187 | 188 | float lerpA = (t - at1) / (at2 - at1); 189 | float lerpC = (t - ct1) / (ct2 - ct1); 190 | float4 finalC = lerp(cv1, cv2, lerpC); 191 | float4 finalA = lerp(av1, av2, lerpA); 192 | finalC.a = finalA; 193 | 194 | return finalC; 195 | } 196 | 197 | v2f vert (appdata_t v) 198 | { 199 | v2f OUT; 200 | UNITY_SETUP_INSTANCE_ID(v); 201 | UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); 202 | OUT.worldPosition = v.vertex; 203 | OUT.vertex = UnityObjectToClipPos(OUT.worldPosition); 204 | 205 | OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex); 206 | OUT.color = v.color * _Color; 207 | 208 | const float Deg2Rad = (UNITY_PI * 2.0) / 360.0; 209 | float rotationRadians = _Rotation * Deg2Rad; 210 | float s = sin(rotationRadians); 211 | float c = cos(rotationRadians); 212 | float2x2 rotationMatrix = float2x2(c, -s, s, c); 213 | OUT.texcoord.xy = mul(v.texcoord.xy, rotationMatrix); 214 | 215 | return OUT; 216 | } 217 | 218 | fixed4 frag (v2f IN) : SV_Target 219 | { 220 | half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color; 221 | 222 | #ifdef UNITY_UI_CLIP_RECT 223 | color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); 224 | #endif 225 | 226 | #ifdef UNITY_UI_ALPHACLIP 227 | clip (color.a - 0.001); 228 | #endif 229 | 230 | float2 texcoord = (IN.texcoord - _MainTex_ST.zw) / _MainTex_ST.xy; 231 | float4 gradient = get_gradient(texcoord); 232 | return color * gradient; 233 | } 234 | 235 | ENDCG 236 | 237 | } 238 | 239 | } 240 | 241 | CustomEditor "Zigurous.UI.Editor.UIGradientShaderGUI" 242 | } 243 | -------------------------------------------------------------------------------- /Shaders/UIGradient.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2dd4e86295075a24c94a2a99d4dceb5e 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.zigurous.ui", 3 | "version": "0.3.0", 4 | "displayName": "UI Toolkit", 5 | "description": "The UI Toolkit package contains scripts and utilities for creating UI in Unity projects. The package is intended to solve common problems that arise when developing UI and menus.", 6 | "unity": "2019.4", 7 | "repository": "https://github.com/zigurous/unity-ui-toolkit", 8 | "documentationUrl": "https://docs.zigurous.com/com.zigurous.ui", 9 | "changelogUrl": "https://docs.zigurous.com/com.zigurous.ui/changelog", 10 | "licensesUrl": "https://docs.zigurous.com/com.zigurous.ui/license", 11 | "keywords": [ 12 | "ui", 13 | "menus", 14 | "navigation", 15 | "scrolling", 16 | "masking", 17 | "letterboxing" 18 | ], 19 | "publishConfig": { 20 | "registry": "https://npm.pkg.github.com/@zigurous" 21 | }, 22 | "author": { 23 | "name": "Zigurous", 24 | "email": "support@zigurous.com", 25 | "url": "https://zigurous.com" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 12b5a06af5493f14cb503e73f1a3d167 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------