-
-
Save FlaShG/ac3afac0ef65d98411401f2b4d8a43a5 to your computer and use it in GitHub Desktop.
using UnityEngine; | |
/// <summary> | |
/// Small helper class to convert viewport, screen or world positions to canvas space. | |
/// Only works with screen space canvases. | |
/// </summary> | |
/// <example> | |
/// <code> | |
/// objectOnCanvasRectTransform.anchoredPosition = specificCanvas.WorldToCanvasPoint(worldspaceTransform.position); | |
/// </code> | |
/// </example> | |
public static class CanvasPositioningExtensions | |
{ | |
public static Vector3 WorldToCanvasPosition(this Canvas canvas, Vector3 worldPosition, Camera camera = null) | |
{ | |
if (camera == null) | |
{ | |
camera = Camera.main; | |
} | |
var viewportPosition = camera.WorldToViewportPoint(worldPosition); | |
return canvas.ViewportToCanvasPosition(viewportPosition); | |
} | |
public static Vector3 ScreenToCanvasPosition(this Canvas canvas, Vector3 screenPosition) | |
{ | |
var viewportPosition = new Vector3(screenPosition.x / Screen.width, | |
screenPosition.y / Screen.height, | |
0); | |
return canvas.ViewportToCanvasPosition(viewportPosition); | |
} | |
public static Vector3 ViewportToCanvasPosition(this Canvas canvas, Vector3 viewportPosition) | |
{ | |
var centerBasedViewPortPosition = viewportPosition - new Vector3(0.5f, 0.5f, 0); | |
var canvasRect = canvas.GetComponent<RectTransform>(); | |
var scale = canvasRect.sizeDelta; | |
return Vector3.Scale(centerBasedViewPortPosition, scale); | |
} | |
} |
Hello @caneva20,
I was not able to reproduce the bug you described in Unity 2018.3.7f1. However, your version produced incorrect results. Is it possible that you are using a newer version of Unity, and that in this version, Canvas properties have changed?
Hi @FlaShG.
I've created a simple project to test both scripts and tested it with 5 different Unity versions (2018.1.9.f2
, 2018.2.20f1
, 2018.3.1f1
, 2018.3.12f1
, 2019.1.4f4
) and in all of those, I could reproduce the bug.
If don't use the Scale With Screen Size
from CanvasScaler
you won't find the bug.
Could you please try it too? Here's the link to the project used, it is very simple and contains both versions
Project instructions:
- Right mouse button: Spawns a dot(green) using my version
- Left mouse button: Spawns a dot(red) using your version
Is it possible that you have something else in your project that is changing the canvas in some way?
You are not using the class as intended. As the summary states, you're supposed to set the resulting values to the RectTransform.anchoredPosition
property. Your example uses Instantiate
, which has different semantics for its position
parameter.
To instantiate objects on the canvas at your mouse position, use
var instance = Instantiate(prefab, canvas.transform);
instance.GetComponent<RectTransform>().anchoredPosition = point;
Using your version breaks my WorldToCanvasPosition
test case.
Ohoo, my bad, sorry.
I haven't really noticed that this was a requirement as my use case was something different.
I was looking for something that could convert a point on the screen to a point on the canvas, but not for anchoring it.
Anyway, thank you for your patience, (feeling like a jerk for saying you had a bug)
No worries ๐
Hi ๐,
awesome gist you have here, just tried it and it works without any issues ๐
How is this licensed, does a specific one apply?
Hi @Kellojo,
they all have no license, you may use them however you want ๐
awesome! saved me a lot of time ๐ฏ
Hey excellent work! it saved me a lot!
This is great! I have a big need to know what a UI Image on the canvas would be in World Space. Could someone please help me with this? A CanvasToWorldPosition. Thank you. -- Jeff
I needed world coordinates of different UI elements that was on a Canvas set to Screen Space - Camera. That was I could move the game objects to the location in the HUD and make them disappear once they reached the location. My game is 2D and it works exactly as I needed now. I hope this helps.
UICamera is my camera used for the UI (Screen Space - Camera) setting. I don't use Overlay or World canvas.
thisCanvas is the canvas in the scene.
var position = UICamera.WorldToScreenPoint(MyUIObject.transform.position); position.z = (thisCanvas.transform.position - UICamera.transform.position).magnitude; theLocationOfUIObjectInWorldSpace = Camera.main.ScreenToWorldPoint(position);
Hello!
This is a little modification in case that the camera is not drawn in the 100% of the screen, using the viewport rect
public static Vector3 WorldToCanvasPosition(this Canvas canvas, Vector3 worldPosition, Camera camera = null, bool useNormalizeViewPort = false)
{
if (camera == null)
{
camera = Camera.main;
}
var viewportPosition = camera.WorldToViewportPoint(worldPosition);
if (useNormalizeViewPort)
{
Rect normalizedViewPort = camera.rect;
viewportPosition.x = viewportPosition.x * normalizedViewPort.width + normalizedViewPort.x;
viewportPosition.y = viewportPosition.y * normalizedViewPort.height + normalizedViewPort.y;
}
return canvas.ViewportToCanvasPosition(viewportPosition);
}
Thanks!
Awsome it works for me, just be care about your canvas anchor point. (from ViewportToCanvasPosition its currently using 0.5 0.5), but in my case its 0 1).
Hey @FlaShG, first off, thank you for the snipet, it really helped me. :)
But I found a BUG with it (and fixed it)TheViewportToCanvasPosition()
does not take into account the scale of the canvas itself, if you are using a CanvasScaler the result would be wrong, by just scaling (again) the result bycanvasRect.localScale
you can fix it.I've forked your snipet and fixed the BUG if you want to take a look at. (or if anyone else wants the fixed version)FIXED VERSION: https://gist.github.com/caneva20/5c5b8950601aac269d34eefbf1be01e9EDIT:
There's no BUG, it was all a mistake of mine