5
\$\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\$
6
  • \$\begingroup\$ Draw it by hand \$\endgroup\$
    – Bálint
    Commented Apr 25, 2017 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\$ Commented Apr 25, 2017 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\$ Commented Apr 25, 2017 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
    Commented Apr 26, 2017 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\$ Commented Apr 27, 2017 at 4:58

2 Answers 2

13
\$\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\$
7
  • \$\begingroup\$ but how we can set some another texture to this sphere? \$\endgroup\$ Commented May 5, 2017 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
    Commented May 5, 2017 at 12:12
  • \$\begingroup\$ I want to place picture behind the UI elements. So, that I could change picture on my sphere... \$\endgroup\$ Commented May 5, 2017 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
    Commented May 5, 2017 at 16:17
  • \$\begingroup\$ Can you include the shader for step number 5 ? \$\endgroup\$
    – Vlad
    Commented Jul 30, 2019 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\$
10
  • \$\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
    Commented Apr 27, 2017 at 5:03
  • \$\begingroup\$ Have you considered using a dedicated UI camera rendering the UI, "No" \$\endgroup\$ Commented Apr 27, 2017 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\$ Commented Apr 27, 2017 at 5:37
  • \$\begingroup\$ The UI objects can be rotated according to the curved mesh and its simply showing correctly. \$\endgroup\$ Commented Apr 27, 2017 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\$ Commented Apr 27, 2017 at 5:39

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .