Tuesday, March 5, 2019

How to make curved GUI in Unity?


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.




Answer



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;

}
}

No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...