3
\$\begingroup\$

I'm really new in Unity. I want to make a curved GUI by myself. How would I make one?

I'm using Unity 5.6.

\$\endgroup\$
  • \$\begingroup\$ Draw it by hand \$\endgroup\$ – Bálint Apr 25 '17 at 9:49
  • 1
    \$\begingroup\$ There is no built in option of Curved GUI, you either can make a curved mesh in Max, Blender or May or using any third party modeling tool. Later you can use this mesh in with Canvas. \$\endgroup\$ – Muhammad Faizan Khan Apr 25 '17 at 10:30
  • 1
    \$\begingroup\$ By "curved" you mean like Iron Man's suit hud? If so, Mohammad's comment is the way to go \$\endgroup\$ – Draco18s Apr 25 '17 at 13:10
  • \$\begingroup\$ @MohammadFaizanKhan, this would be great as an answer if you can give some more details about how one would execute this solution. \$\endgroup\$ – DMGregory Apr 26 '17 at 16:53
  • \$\begingroup\$ @DMGregory time is short to answer. The aim is to convey suggestion and answer is not so difficult. but now i add it upon your comment. \$\endgroup\$ – Muhammad Faizan Khan Apr 27 '17 at 4:58
11
\$\begingroup\$

Here's a strategy for making arbitrarily bent UI: we'll render our UI into a texture (in realtime, not as a baking step), and then map that texture onto whatever mesh we want.

Here's how I made this spherical example:

Example of spherical UI wrapping around he camera.

  1. Create a RenderTexture to store the UI. This needs to be quite high-res to get text looking crisp. I used 4096x2048 because I intend to map the texture along lines of latitude & longitude, and there's twice as much longitude to cover. ;)

    If you're only rendering part of the UI at a time, as in the case of a wraparound sphere, we could use an adaptive window that renders only the visible parts to maximize use of the available texture space, but I'll elide that complexity for now.

  2. Create a second camera to capture your UI

    • set to Orthographic
    • culling mask set to UI only
    • target texture set to your RenderTexture
    • set its Depth to -1 so it renders before your MainCamera
  3. Create your UI canvas

    • set to Screenspace - Camera, using your UI camera
    • remove the Canvas Scaler - it's not needed if we're rendering to a fixed resolution
  4. Set your Main Camera's culling mask to exclude the UI layer (so we don't see it twice)

  5. Set up your mesh. For my example I used a sphere centered on my Main Camera, with a shader that renders the inside faces instead of the outside, and maps the assigned RenderTexture around its surface using latitude & longitude (equirectangular mapping).

  6. To get the UI to respond to input events, you need to customize your input module so it knows how to translate clicks/taps on the screen into corresponding coordinates in your UI rendering space. Here's one I wrote to handle the sphere, based off of the example OpticalOverride provides here.


 using UnityEngine;
 using UnityEngine.EventSystems;

 public class SphericalInputModule : StandaloneInputModule {

    new public Camera camera;
    public RenderTexture uiTexture;

    Vector2 m_cursorPos;
    private readonly MouseState m_MouseState = new MouseState();
    protected override MouseState GetMousePointerEventData(int id = 0)
    {
        MouseState m = new MouseState();

        // Populate the left button...
        PointerEventData leftData;
        var created = GetPointerData(kMouseLeftId, out leftData, true);

        leftData.Reset();

        if (created)
            leftData.position = m_cursorPos;

        // Ordinarily we'd just pass the screen coordinates of the cursor through.
        //Vector2 pos = Input.mousePosition;

        // Instead, I'm going to translate that position into the latitude longitude
        // texture space used by my UI canvas:
        Vector2 trueMousePosition = Input.mousePosition;
        Vector3 ray = camera.ScreenPointToRay(trueMousePosition).direction;

        Vector2 pos;
        pos.x = uiTexture.width * (0.5f - Mathf.Atan2(ray.z, ray.x) / (2f * Mathf.PI));
        pos.y = uiTexture.height * (Mathf.Asin(ray.y) / Mathf.PI + 0.5f);
        m_cursorPos = pos;

        // For UV-mapped meshes, you could fire a ray against its MeshCollider 
        // and determine the UV coordinates of the struck point.

        leftData.delta = pos - leftData.position;
        leftData.position = pos;
        leftData.scrollDelta = Input.mouseScrollDelta;
        leftData.button = PointerEventData.InputButton.Left;
        eventSystem.RaycastAll(leftData, m_RaycastResultCache);
        var raycast = FindFirstRaycast(m_RaycastResultCache);
        leftData.pointerCurrentRaycast = raycast;
        m_RaycastResultCache.Clear();

        // copy the apropriate data into right and middle slots
        PointerEventData rightData;
        GetPointerData(kMouseRightId, out rightData, true);
        CopyFromTo(leftData, rightData);
        rightData.button = PointerEventData.InputButton.Right;

        PointerEventData middleData;
        GetPointerData(kMouseMiddleId, out middleData, true);
        CopyFromTo(leftData, middleData);
        middleData.button = PointerEventData.InputButton.Middle;

        m_MouseState.SetButtonState(PointerEventData.InputButton.Left, StateForMouseButton(0), leftData);
        m_MouseState.SetButtonState(PointerEventData.InputButton.Right, StateForMouseButton(1), rightData);
        m_MouseState.SetButtonState(PointerEventData.InputButton.Middle, StateForMouseButton(2), middleData);

        return m_MouseState;
    }
}
\$\endgroup\$
  • \$\begingroup\$ but how we can set some another texture to this sphere? \$\endgroup\$ – GuardFromUA May 5 '17 at 10:40
  • \$\begingroup\$ Can you describe what effect you want? If it's a dynamic texture of some kind you could add it to the background of your UI canvas, so it gets rendered into the same RenderTexture for display on the sphere. Or you could set up a multi-texture material in the usual way and assign the texture to the sphere itself. Or you could set up a slightly larger concentric sphere and put the other texture on that. Or you could make the other texture your skybox. Lots of ways, depending on what you want it to look like and how you want to work with it. \$\endgroup\$ – DMGregory May 5 '17 at 12:12
  • \$\begingroup\$ I want to place picture behind the UI elements. So, that I could change picture on my sphere... \$\endgroup\$ – GuardFromUA May 5 '17 at 14:39
  • \$\begingroup\$ Your two quickest options are to either add the texture to your UI canvas as a background image, or render it on a second sphere a little bigger than the first. If you need help setting up a multi-texture material, try posting that as a separate question. \$\endgroup\$ – DMGregory May 5 '17 at 16:17
  • \$\begingroup\$ Can you include the shader for step number 5 ? \$\endgroup\$ – Vlad Jul 30 at 2:10
2
\$\begingroup\$

As @DMGregory suggested, here is the answer with some details:

There is no built in option of Curved GUI, you either can make a curved mesh in Max, Blender or May or using any third party modeling tool. Later you can use this mesh in with Canvas.

  1. make a curved mesh in any 3d modeling tools
  2. import it into unity
  3. create canvas
  4. make curved mesh child of the canvas
  5. add buttons or other desired unity ui objects into canvas and place it according to curved mesh.

Here are some images for demonstrations:

A view of curved mesh ui with buttons enter image description here

A view of curved mesh, you can model it according to your requirments enter image description here

\$\endgroup\$
  • \$\begingroup\$ Have you considered using a dedicated UI camera rendering the UI to a texture,then texturing the curved mesh using this resulting texture? You'd have to set up a custom raycaster to get input events working correctly, I think, but it would be a way to get the UI itself truly curving with the mesh rather than being flat elements hovering in front of it. \$\endgroup\$ – DMGregory Apr 27 '17 at 5:03
  • \$\begingroup\$ Have you considered using a dedicated UI camera rendering the UI, "No" \$\endgroup\$ – Muhammad Faizan Khan Apr 27 '17 at 5:36
  • \$\begingroup\$ "You'd have to set up a custom raycaster to get input events working correctly," my click event are working on buttons, i did not added custom raycaster for it \$\endgroup\$ – Muhammad Faizan Khan Apr 27 '17 at 5:37
  • \$\begingroup\$ The UI objects can be rotated according to the curved mesh and its simply showing correctly. \$\endgroup\$ – Muhammad Faizan Khan Apr 27 '17 at 5:38
  • \$\begingroup\$ I have implemented a 3d GUI in this way recently in a project. I also added custom raycast to load buttons event but it was for pointer but a simple on click also work without adding any custom raycast \$\endgroup\$ – Muhammad Faizan Khan Apr 27 '17 at 5:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.