- Home /
[CANVAS] Coordinate transfer with scaling (pictures+details)
Assets
I have included all the necessary information from my project.
The Intended Goal
I think it becomes obvious what I am trying to do; I am trying to hover a cursor on each solar system object that renders on the screen. And then I track it each update.
Problems:
--it seems like my problems really started when I switched the Canvas Render-Scaling to: "Keep pixel constant", now it seems like the coordinate systems are not transferring appropriately.... OR, I am not understanding the anchors enough.
---Also I don't understand the logic behind using the Sun's local coordinates... or it's world coordinates to obtain it's (WorldToScreenPoint). This code seemed to look better when I was using the sun's local coordinates.. But in my mind it doesn't make sense because the camera should care more if the sun is at -8000,0,0..... instead of it's local 0,0,0
--- on the Sprite PreFAB, I seem to switch and try all kinds of things to get this damn thing to work. I have generally understand the difference between keeping a pivot anchor, and stretching...... I guess my biggest mis-understanding was how when choosing the "BOTTOM_LEFT" pivot. Before when my Canvas_scaler was on "Scale with screen size" everything seemed to work pretty good, sort of..... But, once I switched to "Constant pixel size". It's as if the x,y positioning is somehow scaled to the screen? or something....( not just the literal scale )
SpriteHudBehavior script ( on the SpriteHudLayer, a CANVAS_PANEL )
using UnityEngine; using System.Collections; using System;
public class SpriteHudBehavior : MonoBehaviour {
public LayerMask layerToCheckForObjects;
public float distanceToPrepSprite;
public GameObject playerToTrack;
public GameObject spriteToUse;
public bool checkAgain;
public Collider[] trackedEnvironmentColliders;
public Vector3 copyPosition;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
if (checkAgain) // WE CHECK AGAIN ANYTIME we warp into a NEW SOLAR SYSTEM
{
checkAgain = false;
Array.Clear (trackedEnvironmentColliders,0,trackedEnvironmentColliders.Length);
Vector3 thePlayersPosition = playerToTrack.transform.position;
trackedEnvironmentColliders = Physics.OverlapSphere(thePlayersPosition,distanceToPrepSprite, layerToCheckForObjects.value);
}
//ALWAYS RUN THIS
foreach (Collider eachCollider in trackedEnvironmentColliders)
{
copyPosition = eachCollider.gameObject.transform.position;
copyPosition = transform.TransformPoint (copyPosition);
copyPosition = GameObject.FindGameObjectWithTag ("MainCamera").GetComponent <screencoor> ().GiveMeBackValuesfor(copyPosition);
if (copyPosition.x > 0f && copyPosition.y > 0f && copyPosition.x < Screen.width && copyPosition.y < Screen.height && copyPosition.z <0f)
{
//if cursor != exist, then create one
if (eachCollider.GetComponent <EnvironmentFoundation> ().myCursorIsOnScreen == false)
{
GameObject copyOf = Instantiate (spriteToUse, new Vector3 (0f,0f,0f), Quaternion.identity) as GameObject;
copyOf.transform.SetParent (this.transform); // Once Instantiated, set it's parent to the HUDwindow ( this script)
copyOf.GetComponent <RectTransform> ().anchoredPosition3D = new Vector3 (copyPosition.x, copyPosition.y,0f); // Before the frame updates, set it's position to the correct position
//copyOf.GetComponent <RectTransform> ().localScale = new Vector3 (1f,1f,1f); //Using the Canvas-Render "constant pixel size", when we do the transform is also does scale, but we need to keep it original
copyOf.name = "Cursor"; // Before the frame updates, set it's name to Cursor
copyOf.tag = "Cursor"; // Before the frame updates, Set it's tag
copyOf.layer = 9; // Before the frame updates, Set this Sprite to a Layer only visible by the UI
copyOf.GetComponent <CursorTracker> ().myOwner = eachCollider.gameObject; // The cursor needs to remember the object that it gets info from
eachCollider.GetComponent <EnvironmentFoundation> ().myCursorIsOnScreen = true;
}
// So if all cursors are on the screen that need to be:
foreach (GameObject c in GameObject.FindGameObjectsWithTag ("Cursor"))
{
copyPosition = GameObject.FindGameObjectWithTag ("MainCamera").GetComponent <screencoor> ().GiveMeBackValuesfor(copyPosition);
c.GetComponent <RectTransform> ().anchoredPosition3D = new Vector3 (copyPosition.x, copyPosition.y,0f);
}
} else
{
// otherwise if the object is off screen
// THEN: we need to make sure that if has a cursor, it is deleted
if (eachCollider.GetComponent <EnvironmentFoundation> ().myCursorIsOnScreen == true)
{
eachCollider.GetComponent <EnvironmentFoundation> ().myCursorIsOnScreen = false;
foreach (GameObject c in GameObject.FindGameObjectsWithTag ("Cursor"))
{
if (c.gameObject.GetComponent <CursorTracker> ().myOwner != eachCollider.gameObject){continue;}
c.GetComponent <CursorTracker> ().Please86Yourself ();
}
}
}
}
}
}
Screencoord script (on camera)
using UnityEngine; using System.Collections;
public class screencoor : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public Vector3 GiveMeBackValuesfor (Vector3 objectPassTransform)
{
return camera.WorldToScreenPoint (objectPassTransform);
}
}
Environment foundation ( put on all the suns and planets)
using UnityEngine; using System.Collections;
public class EnvironmentFoundation : MonoBehaviour {
public bool myCursorIsOnScreen;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
CursorTracker script (placed on each instantiated cursor)
using UnityEngine; using UnityEngine.UI; using System.Collections;
public class CursorTracker : MonoBehaviour {
public GameObject myOwner;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update ()
{
}
public void Please86Yourself ()
{
Destroy (gameObject);
}
}
Answer by karma0413 · Jan 31, 2015 at 02:08 AM
SOLUTION FOUND 2/1/2015 0644 MOUNTAIN TIME
EDIT: I had to edit a backpost because I can only provide 3 comments to my question
So the problem has nothing to do with my scripts, so the last script update for 1/30/15 is all fine and works well. I will provide you with the web-link that began to shed some light on my problem. As my suspicions were confirmed, there was some sort of scaling issue going on. Link that Shed Some light
My direct solution:
First, I was thinking that I needed to do change all my images to 100x100 as the link points out. But surely, there has to be a better solution. AND THERE IS:
For those of us using a Canvas, which has it's render mode set to: "Keep Constant Pixel Size"
You will notice on the Canvas Editors panel, that you can set pixelsPerUnit as the previous links point out. HOWEVER, if you leave that alone ( because there may be other things on your UI canvas ), and you go to your Assets folder and find the actual sprite for that cursor. Once you click on it, you will find a PerPixelUnit only for that sprite: Image shown below for my Sprite:Cursor_Base:
Once I finished that, I did need to remember that all my cursor sprites were pivoting from bottom_left, just like the screen coordinates. And so logically to get them on center I needed to basically divide the sprite.x&y in half so that it ends up on center. I was actually lazy and did not pull those coordinates directly and instead set them as a value I could edit in the Editor. So you can see in the next picture, that at the very bottom I have values stored for prefabX&y width and height. Once I made them both back to 40x40.
I also deleted the Image(script) and the Canvas(renderer) components and simply left the RectTransform and SpriteHudBehavior script running on it. Mind you, the RectTransform is still left at stretched because I wanted it to stretch through the entire screen width. and height. I could have left these on, but they were not necessary and I did not know that doing this the first time. So I simply removed them. CTRL+mouseWheel to Zoom into photo
Answer by karma0413 · Jan 30, 2015 at 09:22 PM
UPDATE 1/30/15 1342 MTN TIME
Okay so I am continuing to narrow down the problem. I suppose that knowing what is NOT contributing to the problem, may shed some light on where the problem exists.
I have included the code just in case the moderators didnt approve my last message with the code update.
Interesting facts
The [sprite]Cursor X position, is now correctly centered on the planet. I figured out that my problem was the prefabsWidthAndHeight.x was being off-center beacuse I had it set at 40, the width of my sprite. I now set it to 0 so there is no additional modifying math on this X position and for some reason it is now centered on the planet. BUT, there is still some sort of X scaling going on when the camera pans:
copyPosition.x-(prefabsWidthAndHeight.x/2)
Which it makes it interesting when you consider that for the [Sprite]Cursor Y position. It is completely PERFECT! It does not have any weird scaling issues and it is centered perfectly on Y axis. That's not so interesting though. What was interesting to me, is that I use the same code but the prefabsWidthAndHeight.Y is set to 40 ( the height of my sprite in pixels ).
copyPosition.y-(prefabsWidthAndHeight.y/2)
Hmmmmmmmmmmm
Where is this X scaling coming from? I keep guess for hours and hours and while my code I think is becoming more logically strong. I am still not understanding something.
Thanks
Thanks by the way for taking the time to read this lengthy write up, I hope I kept the details concise and structured.
THE INCLUDED SCRIPT SpriteHudLayer.cs
using UnityEngine; using System.Collections; using System; using UnityEditor;
public class SpriteHudBehavior : MonoBehaviour {
public LayerMask layerToCheckForObjects;
public float distanceToPrepSprite;
public GameObject playerToTrack;
public GameObject spriteToUse;
public bool checkAgain;
public Collider[] trackedEnvironmentColliders;
public Vector3 copyPosition;
public Vector2 prefabsWidthAndHeight;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
if (checkAgain) // WE CHECK AGAIN ANYTIME we warp into a NEW SOLAR SYSTEM
{
checkAgain = false;
Array.Clear (trackedEnvironmentColliders,0,trackedEnvironmentColliders.Length);
Vector3 thePlayersPosition = playerToTrack.transform.position;
trackedEnvironmentColliders = Physics.OverlapSphere(thePlayersPosition,distanceToPrepSprite, layerToCheckForObjects.value);
}
//ALWAYS RUN THIS
foreach (Collider eachCollider in trackedEnvironmentColliders) // For every Collider we are tracking
{
GameObject eachTrackedObject = eachCollider.gameObject;
copyPosition = eachTrackedObject.transform.position; // this is already a world coordinate position
copyPosition = GameObject.FindGameObjectWithTag ("MainCamera").GetComponent <screencoor> ().GiveMeBackValuesfor(copyPosition);
//if the object is on the screen, THEN:
Vector2 gameWindow = new Vector2 (Screen.width,Screen.height); //new Vector2(Screen.width,Screen.height); //Handles.GetMainGameViewSize ();// <--put this code in for actual game window???
if (copyPosition.x > 0f && copyPosition.y > 0f && copyPosition.x < gameWindow.x && copyPosition.y < gameWindow.y && copyPosition.z >0f)
{
//if cursor != exist, then create one
if (eachTrackedObject.GetComponent <EnvironmentFoundation> ().myCursorIsOnScreen == false)
{
GameObject copyOf = Instantiate (spriteToUse, new Vector3 (0f,0f,0f), Quaternion.identity) as GameObject;
copyOf.transform.SetParent (this.transform); // Once Instantiated, set it's parent to the HUDwindow ( this script)
copyOf.GetComponent <RectTransform> ().anchoredPosition3D = new Vector3 (copyPosition.x-(prefabsWidthAndHeight.x/2), copyPosition.y-(prefabsWidthAndHeight.y/2),0f); // Before the frame updates, set it's position to the correct position
copyOf.GetComponent <RectTransform> ().localScale = new Vector3 (1f,1f,1f); // Using the Canvas-Render "constant pixel size", when we do the transform is also does scale, but we need to keep it original
copyOf.name = "Cursor"; // Before the frame updates, set it's name to Cursor
copyOf.tag = "Cursor"; // Before the frame updates, Set it's tag
copyOf.layer = 9; // Before the frame updates, Set this Sprite to a Layer only visible by the UI
copyOf.GetComponent <CursorTracker> ().myOwner = eachTrackedObject; // The cursor needs to remember the object that it gets info from
eachCollider.GetComponent <EnvironmentFoundation> ().myCursorIsOnScreen = true;
}
// if the cursor for this object already exists, then just update its position
if (eachTrackedObject.GetComponent <EnvironmentFoundation> ().myCursorIsOnScreen == true)
{
foreach (GameObject c in GameObject.FindGameObjectsWithTag ("Cursor"))
{
if (c.GetComponent <CursorTracker> ().myOwner == eachTrackedObject)
{
copyPosition = eachTrackedObject.transform.position; // this should already be a world coordinate
copyPosition = GameObject.FindGameObjectWithTag ("MainCamera").GetComponent <screencoor> ().GiveMeBackValuesfor(copyPosition);
c.GetComponent <RectTransform> ().anchoredPosition3D = new Vector3 (copyPosition.x-(prefabsWidthAndHeight.x/2), copyPosition.y-(prefabsWidthAndHeight.y/2),0f);
}
}
}
} else
{
// otherwise if the object is off screen
// THEN: we need to make sure that if has a cursor, it is deleted
if (eachTrackedObject.GetComponent <EnvironmentFoundation> ().myCursorIsOnScreen == true)
{
eachTrackedObject.GetComponent <EnvironmentFoundation> ().myCursorIsOnScreen = false;
foreach (GameObject c in GameObject.FindGameObjectsWithTag ("Cursor"))
{
if (c.GetComponent <CursorTracker> ().myOwner != eachTrackedObject){continue;}
c.GetComponent <CursorTracker> ().Please86Yourself ();
}
}
}
}
}
}