Place scene object to get behind UI element - c#

I have an object in my scene's sky and want to prevent it from getting cut out of the view, no matter the screen ratio. So I set up a canvas and a PlaceHolder RectTransform on it, that - as being UI element - gets scaled and repositioned in respect to the screen aspect ratio. My goal is to use this position to determine where to put my scene object by using the following script:
Camera mainCam;
RectTransform placeHolder;
private void Start()
{
mainCam = GameObject.FindGameObjectWithTag("MainCamera")
.GetComponent<Camera>();
placeHolder = GameObject.FindGameObjectWithTag("SkyObjectPlaceholder")
.GetComponent<RectTransform>();
var screenPos = mainCam.ScreenToWorldPoint(
new Vector3(placeHolder.rect.x, placeHolder.rect.y, 0));
transform.position = new Vector3(screenPos.x, screenPos.y,
transform.position.z);
}
The issue is that my sky object is always placed in the center of the screen. It is not a child of anything on the scene, I just put it there. The placeholder is to the upper right in the canvas. Note that I tried playing with anchors for the UI element, set it to be left bottom, right top, no change, the scene object is always in the center of the screen.
What am I doing wrong? How to make a Scene element match up the position of a Canvas element, so it gets right behind it?
Note: I'm using a perspective camera. I don't know if it matters, but I thought I mention it, just in case.
Yet another edit: after messing around with the issue a bit, I found that the problem is possibly linked to the Z input I give the ScreenToWorldPoint, in my case it's 0. I couldn't get a viable Z to input though.

Hmmm... i think you should change the sorting layer of that object

Related

Want to change the Rect Transform of GameObject (Button) on canvas unity?

I am working on learning how to use Unity and have been unable to solve this problem of mine. I am currently working on a prototype of a game and need to change the RectTransform Pos X to a value that I want.
However, so far I was unable to do it. I tried Vector2 and Vector3 converations but I can't seem to get it right.
Here is what I have atm (I know the new RectTransform is doing nothing, bu I am simply out of ideas.):
void Start()
{
changeTransform = GameObject.FindGameObjectWithTag("NextButton").GetComponent<RectTransform>();
changeTransform = new RectTransform();
}
And all I want is to set the Pos X of the rect transform to be 25.
Anything helps. Thanks a lot.
EDIT:
When I change the value of the Pos X in the inspector it gets overwritten back to the value seen in the picture.
EDIT2:
Below is how my buttons look right now. I just want the Next-Button to be aligned with the other buttons on the left side without it chaning its size.
For what you want to achieve
I just want the Next-Button to be aligned with the other buttons on the left side without it chaning its size.
simply wrap your NextButton under another object
- NextButtonContainer
|-- NextButton
=> make NextButton be left aligned within the NextButtonContainer
Now the NextButtonContainer can simply grow like the other buttons, have no visual components at all and allow the next button to be left aligned within it and have a fixed size
I can see that some properties of the Rect Transform component are being overridden by the Content Size Fitter component.
You should remove the Content Size Fitter component from the GameObject or set the horizontal and vertical fit in this component to unconstrained. If this doesn't work, please let me know of what other components are attached to this GameObject.

Unity C# - Screen Space - Overlay Canvas Input.mousePosition is way off, even after applying the canvasScale?

The image with the compass and other UI is actually the top left corner and the tooltip is spawning outside the canvas as you can see.
Ive been searching for hours now on how to fix this:
Im using a Screen Space - Overlay Canvas.
Setting an UI Elements Anchored position through code to make it follow the mouse.
Vector2 anchoredPosition = Input.mousePosition;
rectTransform.anchoredPosition = anchoredPosition;
However this doesnt retun the right position.
I tried Input.mousePosition / Canvas.scaleFactor;
And I also tried Input.mousePosition / canvasRectTransform.localScale.x;
And finally Camera.main.ScreenToViewportPoint(Input.mousePosition);
But again, no luck at all... Free Aspect doesnt fix it either.
Changing the reference width and height doesnt fix it, setting from 16:9 to 16:10 does nothing to help me either.
The UI element is always way outside the actual canvas (camera view).
The UI element itself is a RectTransform with size of 1,1 with the anchor in the middle. Testing it by setting element position to 0,0 shows it perfectly centered.
Can someone please help me?
Thank you.
EDIT:
I got it to work by changing the reference resolution on the Canvas until I saw the canvas scale was exactly 1,1,1. Ofcourse I dont want this reference resolution, but atleast it tells us that it has to do with that:
Mouse position is read on screen coordinates. Canvas position is a combination of the canvas reference resolution, your actual screen size, and the canvas’ scale factor.
Say your screen is 1080p and your canvas reference resolution is 1080p. And the canvas is set to fill your screen. The coordinates will directly line up between mouse position and canvas position.
With different screen resolutions than the reference resolution, this Gets more complex to calculate. But there’s a handy function to handle this for you.
RectTransformUtility.ScreenPointToLocalPointInRectangle
https://docs.unity3d.com/ScriptReference/RectTransformUtility.ScreenPointToLocalPointInRectangle.html

Instantiate prefabs to a canvas at mouse coordinates

In a project I'm currently working on, I am having users click on an area for where they want to place things into the environment later on down the line. I'd like to visualize what they're placing with simple markers placed on the canvas, so that as they add and remove points the markers come and go as well.
I've found some resources on how to start, listing how to instantiate prefabs into the canvas, but it never seems to work for me. I feel it must have something to do with how I'm using coordinates but I'm not entirely sure.
public GameObject markerPrefab;
Then later on in another function
GameObject boatMarker = Instantiate(markerPrefab, Input.mousePosition, Quaternion.identity);
boatMarker.transform.SetParent(GameObject.FindGameObjectWithTag("Canvas").transform, false);
The code runs, and the prefabs do spawn into the scene, but they all appear in the top right hand corner of the canvas, all sort of one on top of the other. Any ideas what I've done wrong here? Also, while I don't want to ask you guys to write my code for me, any suggestions for a jumping point on how to remove specific instances of the prefab later down the line?
The main issue I'ld say is that you are using SetParent with the second parameter false
If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before.
In your case you want to keep the same world space position.
Since your canvas is Screenspace overlay its width and height (in Unity units) match exactly the display/window pixel width and height. Therefore when you do
GameObject boatMarker = Instantiate(markerPrefab, Input.mousePosition, Quaternion.identity);
The object already is in the correct position. To visualize that I just gave it a cube as child so you see it spawns already where I clicked (you can't see the image yet beacuase it's not a child of a Canvas):
What happens if you pass in that false parameter to the SetParent is that it doesn't keep it's current worldspace position but instead keeps its current localPosition and moves to that relative position within its parent. Since it is a Canvas and your prefab probably also using a RectTransform the resulting position depends on a lot of things like the e.g. the pivot and anchor settings of the prefab's RectTransform but also e.g. the Canvas Scaler -> Scale Factor.
If your prefab e.g. is anchored on the center (usually the default) and you click exactly on the center of your window it will appear on the upper right corner instead.
Why?
you click at (windowWidth / 2, WindowHeight / 2). So the prefab is originally spawned here
Than you use SetParent with false so it keeps that position but this time relative to the center of the Canvas
=> Center of canvas position is (windowWidth / 2, WindowHeight / 2) so adding the local prefab coordinates (windowWidth / 2, WindowHeight / 2) results in the final position (WindowWidth, WindowHeight) == upper right corner.
So you could fix that either by making the prefab beiing anchored on the lower left corner
or do not pass false as parameter to SetParent.
boatMarker.transform.SetParent(_canvas.transform);
you could actually then also do it in one single call
Instantiate(markerPrefab, Input.mousePosition, Quaternion.identity, _canvas.transform);
Additionally you should not use FindObjectWidthTag again and again. I would rather only get it once or even reference it via the Inspector if possible:
public GameObject markerPrefab;
[SerializeField] private Canvas _canvas;
private void Awake()
{
// If no canvas is provided get it by tag
if (!_canvas) _canvas = GameObject.FindGameObjectWithTag("Canvas").GetComponent<Canvas>();
}
// Update is called once per frame
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
Instantiate(markerPrefab, Input.mousePosition, Quaternion.identity, _canvas.transform);
}
}
Try This :
1- insert the button into the Canvas
2- get a Gameobject reference for this button
3- try this code after assigning your button to the button variable and the Game Canvas to the code Canvas variable, and it will work fine
public GameObject refButton;
public GameObject canvas;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
GameObject button = Instantiate(refButton, Input.mousePosition,Quaternion.identity);
button.transform.SetParent(canvas.transform);
}
}

Unity 2017: 2D Screen Origin

Super basic question that I cant seem to find an answer to:
All I've done is create a new 2D unity project and added a single sprite to the scene. I add a C# script to the sprite and set its position to a new Vector2(0,0). When run, the sprite moves its center point to the center of the screen, as opposed to the bottom left corner. This is also the case when I set the position of the spite to 0,0 in the editor. I've tried making the sprite a child of the camera, and I've tried using WorldToScreenPoint(). It's been awhile since I've used Unity so I must not be understanding something.
Thanks for the help
Edit:
As suggested here's the single line of code in question, don't think this will add much clarity.
public class NewBehaviourScript : MonoBehaviour {
// Use this for initialization
void Start () {
this.transform.position = new Vector2 (0, 0);
}
// Update is called once per frame
void Update () {
}
}
Unity renders based on the camera's frustum, not an arbitrary screen pixel position (unless you translate such to the frustum through a script).
By default, the camera will "look" at the scene-space origin (0, 0), and if you put your sprite there, of course it will be centered on screen on play.
[EDIT]
BTW, from...
I've tried making the sprite a child of the camera, and I've tried
using WorldToScreenPoint().
... it seems you're trying to put a sprite somewhere in relation to the camera. If that's for GUI, the correct way to do it is not as a child of the camera. Instead, use a screen-space Canvas. Screen-space canvasses, unlike cameras, work based on pixel position, too! =)

How to make scroll map in XNA (2D)?

I have a map, containing many objects in an area sized 5000*5000.
my screen size is 800*600.
how can i scroll my map, i don't want to move all my objects left and right, i want the "camera" to move, But unfortunately i didn't found any way to move it.
Thanks
I think you are looking for the transformMatrix parameter to SpriteBatch.Begin (this overload).
You say you don't want the objects to move, but you want the camera to move. But, at the lowest level, in both 2D and 3D rendering, there is no concept of a "camera". Rendering always happens in the same region - and you must use transformations to place your vertices/sprites into that region.
If you want the effect of a camera, you have to implement it by moving the entire world in the opposite direction.
Of course, you don't actually store the moved data. You just apply an offset when you render the data. Emartel's answer has you do that for each sprite. However using a matrix is cleaner, because you don't have to duplicate the code for every single Draw - you just let the GPU do it.
To finish with an example: Say you want your camera placed at (100, 200). To achieve this, pass Matrix.CreateTranslation(-100, -200, 0) to SpriteBatch.Begin.
(Performing a frustum cull yourself, as per emartel's answer, is probably a waste of time, unless your world is really huge. See this answer for an explanation of the performance considerations.)
Viewport
You start by creating your camera viewport. In the case of a 2D game it can be as easy as defining the bottom left position where you want to start rendering and expand it using your screen resolution, in your case 800x600.
Rectangle viewportRect = new Rectangle(viewportX, viewportY, screenWidth, screenHeight);
Here's an example of what your camera would look like if it was offset off 300,700 (the drawing is very approximate, it's just to give you a better idea)
Visibility Check
Now, you want to find every sprite that intersects the red square, which can be understood as your Viewport. This could be done with something similar to (this is untested code, just a sample of what it could look like)
List<GameObject> objectsToBeRendered = new List<GameObject>();
foreach(GameObject obj in allGameObjects)
{
Rectangle objectBounds = new Rectangle(obj.X, obj.Y, obj.Width, obj.Height);
if(viewportRect.IntersectsWith(objectBounds))
{
objectsToBeRendered.Add(obj);
}
}
Here's what it would look like graphically, the green sprites are the ones added to objectsToBeRendered. Adding the objects to a separate list makes it easy if you want to sort them from Back to Front before rendering them!
Rendering
Now that we found which objects were intersecting we need to figure out where on the screen the will end up.
spriteBatch.Begin();
foreach(GameObject obj in objectsToBeRendered)
{
Vector2 pos = new Vector2(obj.X - viewportX, obj.Y - viewportY);
spriteBatch.Draw(obj.GetTexture(), pos, Color.White);
}
spriteBatch.End();
As you can see, we deduce the X and Y position of the viewport to bring the world position of the object into Screen Coordinates within the viewport. This means that the small square that could be at 400, 800 in World Coordinates would be rendered at 100, 100 on the screen given the viewport we have here.
Edit:
While I agree with the change of "correct answer", keep in mind that what I posted here is still very useful when deciding which animations to process, which AIs to update, etc... letting the camera and the GPU make the work alone prevents you from knowing which objects were actually on screen!

Categories