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;
Related
So I want to make a cube scaling by my mouse drag. However, the cube would be scaling in the up and down direction, I only want to scale in the up direction.
Here is my code, could someone please help me?
// Start is called before the first frame update
void Start()
{
InitialScale = transform.localScale;
InitialPos = transform.localPosition;
mainCamera = Camera.main;
CameraZDistance = mainCamera.WorldToScreenPoint(transform.position).z;
}
void Update()
{
onMouseDrag();
}
private void onMouseDrag()
{
if (Input.GetMouseButton(0)) {
Vector3 MouseScreenPosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, CameraZDistance);
Vector3 mouseWorldPosition = mainCamera.ScreenToWorldPoint(MouseScreenPosition); // Screen point convert to world point
//change transform
float distance = Vector3.Distance(InitialPos, mouseWorldPosition);
transform.localScale = new Vector3(InitialScale.x, distance / 2f, InitialScale.z);
}
}
By default, when you create a gameobject (cube in this case), it's pivot is the center of the cube. So when you scale it in the y direction, it scales up and down equally. To scale only upwards, you want to put the pivot at the bottom of the cube, at position (0.5, 0, 0.5). To change the pivot position, you can do it in the transform section of the inspector panel in the unity editor. Alternative you can also do it through scripting by similarly referencing the transform of that cube.
Edit:
#Llama solution would work (moving the cube upwards as you scale it). I've used this solution before and if I recall correctly, there were no performance issues, however using the pivots would be the proper way to do it and I would assume it would perform better and more smoothly.
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.
I have a class below that I attach to a object in order to make it rotate around its pivot. I sent the pivot of the sprite via the inspector.
This works exactly how I want it too, BUT the issue I am having is that whenever I touch and drag it, and then touch and drag it again, it snaps to a new position.
What I would like for it to do is, when it is rotated and then rotated again, the sprite stays in its same rotation and not snap to a new position and I would like the angle of the sprite to be reset to 0. The next then is that I want the angle to continually rotate. So if I rotate it in the positive direction, the angle should keep increasing in the positive direction and not change..Such as 0---> 360 ----> 720 -----> etc, etc. And then when the mouse is released, the sprite stays in the same position but the angle is now set back to 0. And then when clicked again to rotate, it rotates from that exact position.
Here is my code thus far which works well for rotating, but I would like to modify it to achieve the above scenario. Any help with this?
public class Steering : MonoBehaviour {
float prevAngle,wheelAngle,wheelNewAngle = 0;
public SpriteRenderer sprite;
void Start () {
}
void Update () {
}
public float GetAngle(){
return wheelAngle;
}
void OnMouseDrag(){
Vector3 mouse_pos = Input.mousePosition;
Vector3 player_pos = Camera.main.WorldToScreenPoint(this.transform.position);
mouse_pos.x = mouse_pos.x - player_pos.x;
mouse_pos.y = mouse_pos.y - player_pos.y;
wheelNewAngle = Mathf.Atan2 (mouse_pos.y, mouse_pos.x) * Mathf.Rad2Deg;
if (Input.mousePosition.x > sprite.bounds.center.x) {
wheelAngle += wheelNewAngle - prevAngle;
} else {
wheelAngle -= wheelNewAngle - prevAngle;
}
this.transform.rotation = Quaternion.Euler (new Vector3(0, 0, wheelAngle));
Debug.Log (wheelAngle);
prevAngle = wheelNewAngle;
}
void OnMouseUp(){
prevAngle = wheelNewAngle;
wheelAngle = 0;
}
}
By angle of the sprite, do you mean the rotation? I'm not sure how the position is changing if there's nothing in your code doing that. Does it always move to the same position? I'm having a little trouble visualizing how your system is supposed to look but I hope this helps.
It looks like you might want to store the previous mouse position so you can get the relative amount to rotate each frame.
At the top:
Vector3 prevMousePos = Vector3.zero;
This method will help get the position when the player pressed:
void OnMouseDown(){
prevMousePos = Input.mousePosition;
}
Then in OnMouseDrag() get the difference between the two mouse positions to get the relative position (if you moved the mouse left, right, up, or down since pressing):
Vector3 mouseDiff = Input.mousePosition - prevMousePos;
With this it will use the relative mouse position after pressing instead of the current one, which should smooth things out.
I'm trying to create a RTS game from scratch using Unity engine. I started with this Unity RTS game tutorial and I'm setting up the camera. Basically what I do is, that I generate [x,0,z] vector for movement. I then need to apply it on camera.
It works perfectly if the camera isn't rotated - but it usually IS rotated and must be rotatable.
According to the tutorial and the docs, I can transform vector to camera's point of view using Camera.main.transform.TransformDirection(MyVector). Which is what I do:
//Get mouse position
float xpos = Input.mousePosition.x;
float ypos = Input.mousePosition.y;
//Create vector for movement
Vector3 movement = new Vector3(0, 0, 0);
//horizontal camera movement
if (xpos >= 0 && xpos < ResourceManager.ScrollWidth)
{
movement.x -= ResourceManager.ScrollSpeed;
}
else if (xpos <= Screen.width && xpos > Screen.width - ResourceManager.ScrollWidth)
{
movement.x += ResourceManager.ScrollSpeed;
}
//vertical camera movement
if (ypos >= 0 && ypos < ResourceManager.ScrollWidth)
{
movement.z -= ResourceManager.ScrollSpeed;
}
else if (ypos <= Screen.height && ypos > (Screen.height - ResourceManager.ScrollWidth))
{
movement.z += ResourceManager.ScrollSpeed;
}
//make sure movement is in the direction the camera is pointing
//but ignore the vertical tilt of the camera to get sensible scrolling
movement = Camera.main.transform.TransformDirection(movement);
//Up/down movement will be calculated diferently
movement.y = 0;
However, if I do this, the vertical movement doesn't work for the inital camera rotation, and when I rotate camera, the movement happens at strange speeds.
How can I properly apply the movement vector on camera?
Well, the problem here is that TransformDirection will preserve the lenth of the vector. So if you tilt the camera downwards the overall length get distributed between the y, x and z axis. Since you simply eliminate the y part the vector will get shorter. The more the camera is tilted the more you will lose on the other two axes.
It's usually easier to just rotate a unit vector, modify it as you want and normalize it when done. This unit vector can then be scaled by your actual movement vector. Something like that:
// [...]
Vector3 forward = Camera.main.transform.forward;
Vector3 right = Camera.main.transform.right;
forward.y = 0f;
right.y = 0f; // not needed unless your camera is also rotated around z
movement = forward.normalized * movement.z + right.normalized * movement.x;
// [...]
Another approach is to seperate your two rotations. So by using an empty GameObject that is only rotated around y and have the camera a child of that gameobject. Now the camera will only be rotated around the local x axis to tilt it the way you want. To lat the camera look into a different direction you rotate the empty GameObject around y. That way you can use the transform of the empty GameObject to transform the direction just the way you did in your code since the tilt of the camera doesn't matter.
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/