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:
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.
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
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
Set your Main Camera's culling mask to exclude the UI layer (so we don't see it twice)
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).
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