Make instantiated object visible on canvas - c#

I have a canvas, and I'd like an instantiated object to be positioned and visible on it.
In Scene view, it looks like it's right in the middle of the canvas. In Game view, it's not visible.
GameObject prefab = Resources.Load<GameObject>("cereal");
Vector3 point = new Vector3(0, 0, 10);
GameObject clone = Instantiate(prefab, point, Quaternion.identity) as GameObject;
clone.transform.parent = canvas.transform;
clone.transform.localScale = new Vector3(100f, 100f, 100f);
What am I doing wrong here?

It depends on what render mode of canvas you are using.
Canvas has 3 rendering modes Screen Space - Overlay, Screen Space - Camera and World Space
For Screen Space - Overlay, if you want to show an object in the canvas rect you should use Camera.ScreenToWorldPoint() to convert the screen position to the world, but you must know where on the screen you want to see the object. Screen position it's position, where x in range 0 - Screen.width, y in range 0 - Screen.height. You must set the distance to the camera yourself.
Example:
//Get the center of the screen;
var centerOfTheScreen = new Vector3(Screen.width/2, Screen.height/2);
var camera = Camera.main;
var distance = camera.transform.forward * 10;// here you can set prefered distance;
var objectPosition = camera.ScreenToWorldPoint(centerOfTheScreen) + distance;
var instance = Instanitate(prefab, objectPosition, Quaternion.identity);
For Screen Space - Camera you can use world position of the camera. In this mode no need to convert screen to world position, camera position will be enough. You can use distance calculation form the previous mode (var distance = camera.transform.forward * 10;) and add it to the camera position. It`s will looks like:
var camera = Camera.main;
var distance = camera.transform.forward * 10;// here you can set prefered distance;
var objectPosition = camera.transform.position + distance;
var instance = Instanitate(prefab, objectPosition, Quaternion.identity);
For World Space you can directly use world position of the canvas gameobject, but, I think, it`s not you case. Just use canvas position instead camera.
Example:
//This code snippet create object benind canvas. If you want create object on canvas - remove distance
var distance = canvas.transform.forward * 10;// here you can set prefered distance;
var objectPosition = canvas.transform.position + distance;
var instance = Instanitate(prefab, objectPosition, Quaternion.identity);
UPD: If you want to make an object the child of a canvas transform, you can use instance.transform.SetParent(canvas.transform, false) and after that use the calculation described above. For the World Space canvas it will be even easier.

Related

Finding the lowest y point of screen bounds

I am using c# in unity and I need to calculate the lowest y position of the camera (2D) however I only know how to find the height i.e.
private Vector2 screenBounds;
void Start()
{
screenBounds = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, Camera.main.transform.position.z));
}
void Update()
{
screenBounds = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, Camera.main.transform.position.z));
if (transform.position.y < screenBounds.y)
Destroy(this.gameObject);
}
}
I am trying to use this code to despawn old objects.
What you get is the top-right corner
Screenspace is defined in pixels. The bottom-left of the screen is (0,0); the right-top is (pixelWidth,pixelHeight).
Also I assume your camera position is e.g. z = -10 and you want a world point in front and not behind the camera
So if you want the bottom border you would rather use
screenBounds = Camera.main.ScreenToWorldPoint(new Vector3 (0, 0, -Camera.main.transform.position.z);
If your camera is orthogonal anyway then you don't have to care about the z at all and can just use
screenBounds = Camera.main.ScreenToWorldPoint(Vector2.zero);
In order to not have to calculate twice if you also want to check later if it is above the screen you could also go the other way round though which is often even easier:
// Other than the ScreenToWorldPoint here it doesn't matter whether
// the camera is orthogonal or perspective or how far in front of the camera you want a position
// or if your camera is moved or rotated
var screenPos = Camera.main.WorldToScreenPoint(transform.position);
if(screenPos.y < 0) Debug.Log("Below screen");
else if(screenPos.y > Screen.height) Debug.Log("Above screen");
if(screenPos.x < 0) Debug.Log("Left of screen");
else if(screenPos.x > Screen.width) Debug.Log("Right of screen");
However, using only the transform.position is a bit unreliable since you already would destroy objects that are still (half) visible.
Instead you could use GeometryUtility.CalculateFrustumPlanes to get the planes surrounding the actual camera frustum and then use GeometryUtility.TestPlanesAABB to check wether your objects' Renderer.bounds are actually visible to the camera like e.g.
Renderer _renderer;
Camera _camera;
void Update()
{
if(!_renderer) _renderer = GetComponent<Renderer>();
if(!_camera) _camera = Camera.main;
var planes = GeometryUtility.CalculateFrustumPlanes(_camera);
if (!GeometryUtility.TestPlanesAABB(planes, _renderer.bounds))
{
Debug.Log($"\"{name}\" is outside of the screen!", this);
// and e.g.
Destroy(gameObject);
}
}
If I understand correctly you want to save the initial box the camera sees and use that as a boundary?
If that's the case then it's easy to do in 2D, but gets much more complex in 3D. So below is the 2D solution.
The camera is in the center of the viewport. Meaning that the top boundry is the Camera position plus half the height of the viewport.
// Get the camera height
float height = Screen.height;
// Now we get the position of the camera
float camY = Camera.Main.transform.position.y;
// Now Calculate the bounds
float lowerBound = camY + height / 2f;
float upperBound = camY - height / 2f;

how to find a position of an Object from World Space and convert to Canvas UI with render mode : Screen Space - Camera in Unity 2d?

I am working in a Game which is pretty similar to Mario. So when player touches the coin object in World Space, I need to animate by moving that coin object to Coin meter, when the render mode of Canvas is Screen Space - Overlay, I can get the sprite object position easily with below code
CoinSprite Code
GameObject coinCanvasObject = Instantiate(prefab, canvas.transform);//Instantiate coin inside Canvas view
coinCanvasObject.transform.position = Camera.main.WorldToScreenPoint(coinSpriteObject.transform.position);//getting coin position from World Space and convert to Screen Space and set to coinCanvasobject position
AnimateCoin animate = coinCanvasObject.GetComponent<AnimateCoin>();
animate.animateCoin(coinSpriteObject.transform.position);
coinSpriteObject.SetActive(false);
AnimateCoin
public class AnimateCoin : MonoBehaviour
{
private float speed = 0f;
private bool isSpawn = false;
private Vector3 screenPos;
public void animateCoin(Vector3 screenPosTemp, Camera cam, Canvas canvas)
{
screenPos = Camera.main.WorldToScreenPoint(screenPosTemp);
isSpawn = true;
}
private void Update()
{
if (isSpawn)
{
speed += 0.025f;
transform.position = Vector3.Lerp(screenPos, targetObject.transform.position, speed);
if (Vector3.Distance(transform.position, targetObject.transform.position) <= 0)
{
StartCoroutine(deActivateCoin());
}
}
}
private IEnumerator deActivateCoin()
{
isSpawn = false;
yield return new WaitForSeconds(0.2f);
gameObject.SetActive(false);
}
}
Since I need to bring particle effect into Canvas view, I am changing the Canvas render mode to Screen Space - Camera.
When I change the Canvas to this render mode I could not get the exact sprite object position to trail the coin effect.
Hope this helps:
public Camera cam; // Camera containing the canvas
public Transform target; // object in the 3D World
public RectTransform icon; // icon to place in the canvas
public Canvas canvas; // canvas with "Render mode: Screen Space - Camera"
void Update()
{
Vector3 screenPos = cam.WorldToScreenPoint(target.position);
float h = Screen.height;
float w = Screen.width;
float x = screenPos.x - (w / 2);
float y = screenPos.y - (h / 2);
float s = canvas.scaleFactor;
icon.anchoredPosition = new Vector2(x, y) / s;
}
PD: It worked perfectly for me in a 2D video game, I didn't test it in a 3D game, but I think it should work too.
I rewrote my previous solution because it might not work correctly on some devices with non-standard resolutions.
This code should always work.
uiObject.anchoredPosition = GetUIScreenPosition(myPin.position, cam3d, uiObject.anchorMin);
public static Vector2 GetUIScreenPosition(Vector3 obj3dPosition, Camera cam3d, Vector2 anchor)
{
Vector2 rootScreen = _rootCanvasRect.sizeDelta;
Vector3 screenPos = cam3d.WorldToViewportPoint(obj3dPosition);
return (rootScreen * screenPos) - (rootScreen * anchor);
}
We take the sizeDelta of our UI Canvas, because it may differ from the screen resolution of the device.
Then we cast the WorldToViewportPoint from our 3d camera to get the relative position on the screen in the format from 0 to 1 by X and Y.
With anchors in the lower left corner ((0,0)(0,0)) this is our final anchoredPosition. However with anchors for example in the center ((0.5,0.5)(0.5,0.5)) we need to adjust the positions by subtracting half the canvas size.
In this example, we will get an unpredictable result when using different min and max anchors in the final object. For example ((0,25,0.25)(0.75,0.75)). But I sincerely doubt that you really need such anchors on an object with a dynamic position depending on the 3d object.

Is there a way to stabilize the 3D object on the Augmented Image

I'm setting up a scene and need my object to appear on one corner of the augmented image.
I've created a script to find the augmented image and set it as an anchor. domPrefab is instantiated on the anchors' position
if (image.TrackingState == TrackingState.Tracking && visualizer == null)
{
//First: detect the augmented image
Anchor imageAnchor = image.CreateAnchor(image.CenterPose);
visualizer = Instantiate(AugmentedImageVisualizerPrefab, imageAnchor.transform, imageAnchor);
visualizer.Image = image;
m_Visualizers.Add(image.DatabaseIndex, visualizer);
//Second: Instantiate the prefabs
GameObject prefab = domPrefab;
var domObject = Instantiate(prefab, anchor.transform.position, anchor.transform.rotation);
domObject.transform.Rotate(0, k_ModelRotation, 0, Space.Self);
domObject.transform.SetParent(anchor.transform, true);
}
I expect the domPrefab to be instantiated in the center of the augmented image, and when I will learn how to place it in the corner, then have the domPrefab appear in the corner of the image.
The actual result is: the domPrefab appear in the middle of the image but it's not stable. When camera looks around it moves a bit and its' scale changes.
Because you are creating the anchor in the center of the image with this line
Anchor imageAnchor = image.CreateAnchor(image.CenterPose);
Then instantiate your visualizer at imageAnchor position and then try to rotate move or something but instead you can do this in your AugmentedImageVisualizer. To do this
Create an empty object and attach AugmentedImageVisualizer script to it.
Create a public GameObject myModel in AugmentedImageVisualizer
Put your model as a child to this object and drag it to myModel in inspector.
Create a prefab with this object
Modify your AugmentedImageVisualizer like this:
public AugmentedImage Image;
public GameObject myModel;
public void Update()
{
float halfWidth = Image.ExtentX / 2;
float halfHeight = Image.ExtentZ / 2;
// for upper left
myModel.transform.localPosition = (halfWidth * Vector3.left) + (halfHeight * Vector3.back);
//for upper right
myModel.transform.localPosition = (halfWidth * Vector3.right) + (halfHeight * Vector3.back);
//for lower left
myModel.transform.localPosition = (halfWidth * Vector3.left) + (halfHeight * Vector3.forward);
//for lower right
myModel.transform.localPosition = (halfWidth * Vector3.right) + (halfHeight * Vector3.forward);
}
Use this as AugmentedImageVisualizer in your AugmentedImageExampleController
If you want it to be stable and not change position over time, do the operation in fifth step in Start instead of Update or in another function and make sure it runs only once.
I hope this helps Good Luck!

Keep a particlesystems transform and cone radius relative to its parent

I have a "Lift". While being in the game, you walk into the particle system and get moved up in the air (on y).
So the particle system is a child of the cube / the lift. So when scaling the cube, I don't want to change the settings of the particle system. It should scale itself on its own.
When the cube got the y position on 5 and a height / scaling on y of 10, the particle system should place itself down at the bottom.
As you can see, I want it being full automatic.
So, when heading into the code I got this
[SerializeField]
ParticleSystem liftParticles;
private void Start()
{
Vector3 objectScale = transform.localScale; // cube scaling
Vector3 particlePos = liftParticles.transform.position; // temp position
particlePos.y = (particlePos.y - objectScale.y) / 2; // move down on y
liftParticles.transform.position = particlePos; // set particle position
float transformScalingX = objectScale.x; // x scaling of the cube
float transformScalingZ = objectScale.z; // z scaling of the cube
var shape = liftParticles.shape; // set the cone radius now
shape.radius = transformScalingX > transformScalingZ ? transformScalingX : transformScalingZ;
liftParticles.shape = shape;
}
I want to go with the following example as mentioned above..
The cube got a scaling of (3,10,3) and its position is (0,5,0)
my current calculation particlePos.y returns a value of -0,75 but it has to be -0,5.
So do I have an error in my code? (yes obviously I do ...)
The second problem is, how do I change the radius of the particlesystem? When trying to reference the radius of the cone, it says I can't change it, it is readonly.
Is it? I hope I can change this somehow ...
Edit:
Obviously, the particlesystem just has to be always on -0,5f on y when having a scaling of (1,1,1). No need for a calculation anymore.
But I still need to change the radius of the shape and set the lifetime of the particles relative to the height of the lift. Means
private void Start()
{
Vector3 liftScale = transform.localScale; // Liftscaling
var shape = liftParticles.shape; // temp shape
shape.radius = liftScale.x > liftScale.z ? liftScale.x : liftScale.z; // set radius
liftParticles.shape = shape; // assign the temp shape to the real shape
liftParticles.main.startLifetime = ; // set the liftetime of the particles relative to its parent height on y
}
As I understand you made the particle system child of the lift (a cube) so it can move together. In case you just want that both of them move together, but they scale independently, you should consider to use an Empty GameObject as a parent.
You can placed this Empty GameObject in the middle of the Cube (your lift) and then make the lift and the particle filter children of that Empty GameObject. Then move the Empty GameObject instead of the lift, and the children will move as well.
About modifying the radio, try this script
GameObject myParticleGenerator;
ParticleSystem.ShapeModule pShape;
pShape = yParticleGenerator.GetComponent<ParticleSystem>().shape;
pShape.radius = 4.0f;

Fixing the camera's left side in a 2D Game in all screen resolutions

I just started making a 2D game using unity and I have a resolution & cam problem.
I have a 2D terrain and i'm trying to fix the left side of the cam to the left side of the terrain. However, when I change the screen resolution to iphone 4 for example, the left side is hidden. I tried this script for the cam but it's not working :
void Awake()
{
Ray ray = Camera.main.ScreenPointToRay(new Vector3(Screen.width/ 2, Screen.height / 2,-10));
Camera.main.orthographicSize = ray.origin.y;
transform.position = new Vector3 (ray.origin.x, ray.origin.y,-10);
}
You want to use Camera.pixelRect (also see Camera.pixelWidth and Camera.pixelHeight).
You're finding the world point located at the center of the screen in world coordinates, changing the camera's viewport size based on that, and then setting the camera's position back to the center of the screen. If you want to anchor the viewport's left edge to say x = 0 (assuming your terrain's left edge is at x = 0) you should find the viewport's xMax, convert it from screen coordinates to world coordinates, and set the camera's x position to that.
void Awake()
{
// Set the camera's orthographic size.
// The camera's pixelWidth, pixelHeight, and pixelRect is relative to it.
Vector3 worldCenter = Camera.main.ScreenToWorldPoint (new Vector3 (Screen.width / 2, Screen.height / 2, -10));
Camera.main.orthographicSize = worldCenter.y;
// Get the viewport's rectangle and calculate the camera's desired position.
// We want to use viewportRect.xMax since we want to move the camera to the right by half of the view width.
Rect viewportRect = Camera.main.pixelRect;
Vector3 cameraPosition = new Vector3 (viewportRect.xMax, 0, 0);
cameraPosition = Camera.main.ScreenToWorldPoint (cameraPosition);
cameraPosition.z = -10;
transform.position = cameraPosition;
}
Some references:
http://docs.unity3d.com/ScriptReference/Camera.html
http://docs.unity3d.com/ScriptReference/Camera-orthographicSize.html
http://forum.unity3d.com/threads/screen-width-vs-camera-pixelwidth.136703/

Categories

Resources