Jump to content
  • Advertisement
  • 12/02/18 10:30 PM

    How to Implement Custom UI Meshes in Unity

    Engines and Middleware

    Christopher Mielack

    You want to create custom meshes for your Unity3D UI, but you found the documentation lacking?

    In this article, I will describe

    • How to implement a bare-essentials custom Unity UI mesh
    • Point out all the pitfalls that leave you looking at invisible or non-existent meshes

     

    TL;DR

    • To implement your own UI mesh, derive from MaskableGraphic and implement OnPopulateMesh().
    • Don’t forget to call SetVerticesDirty/SetMaterialDirty upon changes to texture or other editor-settable properties that influence your UI-elements and should trigger a re-rendering.
    • Don’t forget to set the UIVertex’s color, otherwise, you won’t see anything due to alpha=0, i.e. full transparency.
    • You can look at the full, minimal code example, here.

     

    Of Rendering Mini Maps Inside Unity UI

    My use case was simple: I wanted to create level previews for my current puzzle game project Puzzle Pelago, and I wanted to try making a simple tiling system based on a custom UI mesh. The requirements I was eyeing was that it should behave like all the other UI elements in unity, i.e. it should fit inside its RectTransform, it should work inside a masked ScrollView, and it should respond to disabled state tinting since it would be living inside of a button.  

    What I ended up with looks something like this:

    Screenshot.png

     

    The path there was not that bad, but still frustrating at times since all I found online was forum posts and Unity's own source code to go off of. So here I want to build a simplified example in which we will render a grid of textured quads inside a UI element, using one script. This should take all the hurdles for building any kind of (flat, 2d) UI geometry you might want to build. 

     

    Unity Scene Setup

    Alright, let’s set up the scene as follows: 

    1. Open the Unity project and Scene you want to work in. If there is no Canvas in the scene yet, create one! For this tutorial, I left all the properties at default.
    2. Inside the Canvas, create a ScrollView - we will want to check that our new UI component works inside of that!
    3. Inside the ScrollView > Viewport > Content, create new empty game object - let’s call it MyUiElement
    4. Add a CanvasRenderer component to the new game object, and then add new script: MyUiElement
    5. Open the new script in your favourite c# editor (I love Rider btw.), and go back to Unity’s scene.
    6. To make our lives easier, we will want to set the Scene View’s render mode to “Shaded Wireframe” so we can see our UI mesh geometry in detail. Also, it is useful to switch to the 2D view perspective, select our “MyUiElement” object and press F, so unity zooms in just right.

     

    Screenshot2.pngScreenshot3.png

     

    Implementing the Custom Unity UI Mesh Script in C#

    Now we can go ahead and implement our new C# script!

    First off, our new script needs to at least derive from Graphic . But, if masking inside of ScrollViews, for example, needs to work, we better derive from MaskableGraphic. Otherwise, our graphics will render outside of the mask, too. Lol.

    Also, we want to be able to set the size of the grid cells in the editor, so we should add a public field for that.

    public class MyUiElement : MaskableGraphic
    {
        public float GridCellSize = 40f;

     

    Next, we want to be able to use a texture for our UI elements. Looking at Unity’s own implementation, e.g. that of the Graphic (source code) base class or the default Image (source code) UI element, we can see that a common pattern is to …

    • … define Texture/Material slots as properties, such that when the texture is changed in the inspector, we can trigger Unity UI to re-render even while in edit mode. This is done by calling SetMaterialDirty() and SetVerticesDirty().
    • … implement mainTexture as a default overridden property such that if no texture is provided, we return the default white texture.
        [SerializeField]
        Texture m_Texture;
        
        // make it such that unity will trigger our ui element to redraw whenever we change the texture in the inspector
        public Texture texture
        {
            get
            {
                return m_Texture;
            }
            set
            {
                if (m_Texture == value)
                    return;
     
                m_Texture = value;
                SetVerticesDirty();
                SetMaterialDirty();
            }
        }
        public override Texture mainTexture
        {
            get
            {
                return m_Texture == null ? s_WhiteTexture : m_Texture;
            }
        }

     

    Next, we have to override OnPopulateMesh() to do our rendering. It takes a useful little helper object for building meshes, the VertexHelper , as its argument. It tracks the vertex indices for you, and lets you add vertices, uvs and tris without having to do lots of array arithmetic and index tracking. It must be Clear()’ed before building a new mesh.

    I found it useful (and you may, too) to use a little quad-making helper function, AddQuad():

    // helper to easily create quads for our ui mesh. You could make any triangle-based geometry other than quads, too!
        void AddQuad(VertexHelper vh, Vector2 corner1, Vector2 corner2, Vector2 uvCorner1, Vector2 uvCorner2)
        {
            var i = vh.currentVertCount;
                
            UIVertex vert = new UIVertex();
            vert.color = this.color;  // Do not forget to set this, otherwise 
    
            vert.position = corner1;
            vert.uv0 = uvCorner1;
            vh.AddVert(vert);
    
            vert.position = new Vector2(corner2.x, corner1.y);
            vert.uv0 = new Vector2(uvCorner2.x, uvCorner1.y);
            vh.AddVert(vert);
    
            vert.position = corner2;
            vert.uv0 = uvCorner2;
            vh.AddVert(vert);
    
            vert.position = new Vector2(corner1.x, corner2.y);
            vert.uv0 = new Vector2(uvCorner1.x, uvCorner2.y);
            vh.AddVert(vert);
                
            vh.AddTriangle(i+0,i+2,i+1);
            vh.AddTriangle(i+3,i+2,i+0);
        }
    
        // actually update our mesh
        protected override void OnPopulateMesh(VertexHelper vh)
        {
            // Let's make sure we don't enter infinite loops
            if (GridCellSize <= 0)
            {
                GridCellSize = 1f;
                Debug.LogWarning("GridCellSize must be positive number. Setting to 1 to avoid problems.");            
            }
            
            // Clear vertex helper to reset vertices, indices etc.
            vh.Clear();
            
            // Bottom left corner of the full RectTransform of our UI element
            var bottomLeftCorner = new Vector2(0,0) - rectTransform.pivot;
            bottomLeftCorner.x *= rectTransform.rect.width;
            bottomLeftCorner.y *= rectTransform.rect.height;
    
            // Place as many square grid tiles as fit inside our UI RectTransform, at any given GridCellSize
            for (float x = 0; x < rectTransform.rect.width-GridCellSize; x += GridCellSize)
            {
                for (float y = 0; y < rectTransform.rect.height-GridCellSize; y += GridCellSize)
                {
                    AddQuad(vh, 
                        bottomLeftCorner + x*Vector2.right + y*Vector2.up,
                        bottomLeftCorner + (x+GridCellSize)*Vector2.right + (y+GridCellSize)*Vector2.up,
                        Vector2.zero, Vector2.one); // UVs
                }
            }
            
            Debug.Log("Mesh was redrawn!");
        }

     

    Note that in the AddQuad() function, we set position, uv, and color! Since in the UI material, texture is multiplied with the color by default. Leaving this at default, i.e. (r=0,g=0,b=0,a=0), this will yield 100% transparent material. So all you see is nothing, and if you are wondering why, this might be it. Here we use the component’s inherited color slot.

    Since we want our grid to update whenever the RectTransform is resized, we should also override OnRectTransformDimensionsChange():

        protected override void OnRectTransformDimensionsChange()
        {
            base.OnRectTransformDimensionsChange();
            SetVerticesDirty();
            SetMaterialDirty();
        }

     

    This should do. Now, back to our Unity scene, we should see a grid of white squares inside our RectTransform. To change this, we can select one of unity’s default textures in our texture slot.

    Screenshot4.png

     

    Adjusting the size of the RectTransform or the value of our Grid Cell Size, we can see that the grid updates automatically. Going into play mode, we should also be able to drag around the scroll view’s contents and have the grid be masked correctly. 

    Screenshot5.png Screenshot6.png

    Screenshot7.png

     

    CONCLUSION

    You can have a look at the full code example, here

    Of course, we are not limited to rendering quads, either, since the basic geometry we created here consist of triangles. So any 2D mesh should be possible to draw, and in principle, it could be animated, too! 

    Anyway, if anything in my writeup is unclear, don’t hesitate to ask questions in the comments or via Twitter, @hallgrimgames. 

    Good luck with your project!

     

    Note: This article was originally published on the Hallgrim Games blog, and is republished here with the kind permission of the author Christopher.



      Report Article


    User Feedback


    There are no comments to display.



    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

  • Advertisement
  • Advertisement
  • Latest Featured Articles

  • Featured Blogs

  • Advertisement
  • Popular Now

  • Similar Content

    • By intenscia
      mod.io is an cross platform mod service created by the team behind ModDB.com and IndieDB.com. It can be integrated in-game using the  REST API, C/C++ SDK or engine plugins (if available) Unity is ready and Unreal Engine is in development.
      Features include:
      Platform agnostic (support 1 click mod installs on Steam, Epic Games Store, Discord, GOG, itch.io and even consoles in the future) Clientless (mod.io has no other dependencies and works behind the scenes in your game) Embeddable web app UI, so you can integrate your mod community anywhere Powerful search, filtering and tagging of mods Moderation and reporting systems built-in Steps to getting mod.io integrated:
      Add your game to our test environment or production Read our API documentation for an overview of how mod.io works Choose an Engine Plugin, API or SDK to integrate mod.io into your game and mod making tools Ready to launch? Add your game to our production environment then let's discuss promoting your release Need help? Our team is available on Discord to assist and our getting started guide has more information for you  
       
      Benefits of using mod.io:
      mod.io offers the same core functionality as Steamworks Workshop (1 click mod installs in-game), plus mod hosting, moderation and all of the critical pieces needed. Where we differ is our approach to modding and the flexibility a REST API offers. For example: 
      Our API is not dependent on a client or SDK, allowing you to run mod.io in many places such as your homepage and launchers Designing a good mod browsing UI is hard, our plugins ship with a UI built in to save you a lot of effort and help your mods stand out We don’t apply rules globally, so if you want to enable patronage, sales or other experimental features, reach out to discuss Our platform is built by the super experienced ModDB.com team and is continually improving for your benefit Your community can consume the mod.io API to build modding fan sites or discord bots if they want Large studios and publishers:
      A private white label option is available to license, if you want a fully featured mod-platform that you can control and host in-house. Contact us to discuss.
      Find out more:
      Visit mod.io | About us | Add your game | Chat on Discord
      These screenshots are from our Unity plugin:




    • By squirrelboy1225
      Hi! We recently released an alpha demo for our typing dungeon crawling game, Backspace Bouken. You can find it on itch.io, and we also have a Steam page where you can wishlist the full game. We also have a proper Gamedev.net project here: 
      We've been working on this game for about 10 months and will be releasing it in 2019. You can find our mailing list/social media info on our website if you want to follow along with development!
       
    • By horror_man
      Hello, I'm currently searching for additional talented and passionate members for our team that's creating a small horror game.
       
      About the game: The game would be a small sci-fi/post-apocalyptic survival horror 3D game with FPS (First person shooter) mechanics and an original setting and story based in a book (which I'm writing) scene, where a group of prisoners are left behind in an abandoned underground facility. It would play similar to Dead Space combined with Penumbra and SCP: Secret Laboratory, with the option of playing solo or multiplayer.
       
      Engine that'd be used to create the game: Unity
       
      About me: I'm a music composer with more than 4 years of experience and I'm fairly new in this game development world, and I'm currently leading the team that'd be creating this beautiful and horrifying game. I decided that making the book which I'm writing into a game would be really cool, and I got more motivated about doing so some time ago when I got a bunch of expensive Unity assets for a very low price. However, I researched about how to do things right in game development so I reduced the scope of it as much as I could so that's why this game is really based in a scene of the book and not the entire thing. Also I'm currently learning how to use Unity and learning how to program in C#.
       
      Our team right now consists of: Me (Game Designer, Creator, Music Composer, Writer), 2 3D Modelers, 5 Game Programmers, 1 Sound Effect Designer, 1 3D Animator and 2 2D Artists.
       
      Who am I looking for: We are looking for a talented and passionated 3D Environment Artist that's experienced in the modeling of closed environments and is familiar with the horror and sci-fi genre.
      Right now the game is in mid development and you can see more information about it and follow our progress in our game jolt page here: https://gamejolt.com/games/devilspunishment/391190 . We expect to finish some sort of prototype in 3 months from now.
       
      This is a contract rev-share position
       
      If you are interested in joining, contributing or have questions about the project then let's talk. You can message me in Discord: world_creator#9524
    • By INTwindwolf
      THE PROJECT

      INT is a 3D Sci-fi RPG with a strong emphasis on story, role playing, and innovative RPG features such as randomized companions. The focus is on the journey through a war-torn world with fast-paced combat against hordes of enemies. The player must accomplish quests like a traditional RPG, complete objectives, and meet lively crew members who will aid in the player's survival. Throughout the game you can side and complete missions through criminal cartels, and the two major combatants, the UCE and ACP, of the Interstellar Civil War.
      Please note that all of our current positions are remote work. You will not be required to travel.
      For more information about us, follow the links listed below.
      INT Official website
      IndieDB page
      Also follow social media platforms for the latest news regarding our projects.
      Facebook
      Twitter
      CURRENT OPEN POSITIONS
      Website Manager
      3D Character Modeller
      3D Environment Modeller
      3D Animator
      Unity Engine Programmer
      REVENUE-SHARE
      The project is marching increasingly closer to be ready for our crowd-funding campaign. Being an Indie team we do not have the creative restrictions often imposed by publishers or other third parties. We are extremely conscientious of our work and continuously uphold a high level of quality throughout our project.
      We are unable to offer wages or per-item payments at this time. However revenue-sharing from crowd-funding is offered to team members who contribute 15-20 hours per week to company projects, as well as maintain constant communication and adhere to deadlines. Your understanding is dearly appreciated.
      TO APPLY
      Please send your Cover Letter, CV, Portfolio (if applicable), and other relevant documents/information to this email: JohnHR@int-game.net
      Thank you for your time! Please feel free to contact me via the email provided should you have any questions or are interested to apply for this position. We look forward to hearing from you!
      John Shen
      HR Lead
      Starboard Games LLC
    • By DreamcityClass
      Dream City: Classified – "Survival Code" (Proof of Concept Framework)
      Episodic, 3D 3rd Person Co-op, Action Adventure Puzzle Plat-former
      Hey everyone I'm looking for a PART TIME/ HOBBYIST PROGRAMMER with an interest in the "Afropunk" style and culture. He would need a understanding of Unity python and marching cubes, or a willingness to learn it. I'm a character artist/ animator dabbling in coding and while starting to develop this game myself I just realized I don't have the time. I need help. I need a team. Hopefully some what passionate, but any little bit will help. You covering the coding would free me up to do more art, animation and character design, (and find more guys).
       
      The game is a procedural puzzle game, which aims to make all of the 5 (or more) characters on screen use different methods of traversal and fighting styles. The more characters in the party the more complicated the puzzles get. The players need to work together to survive (Dark Souls combat difficulty). There are charts, diagrams, and examples of each with assets I've already created, and a frame work you just need to stitch together. 
       
      But don't fear, this is a "BY THE EPISODE PROJECT", each of which will be individually Kickstarter'd. Once you sign on we will begin to understand one another's work habits, schedules, etc. while we make this FRAMEWORK. The framework is what we CROWD FUND for support to make the first episode.  
       
      If there is anyone out there interested in...
      Bringing more diversity to Indie games Working with an unique horror adventure world (World Anvil WIP) Working with an committed artist (who understands coding) and a remote growing team Developing a tight development plan, with passive income contracts: Patreon, product sales, (micros) and of course Revenue sharing Interested in working on a co-op TRINE ~like game mixed with DARK SOULS Willing to grow with this me/(us) as this company takes off.  
      Lets make a dream worth dreaming. 
       
      (Contact with questions)


×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!