- Home /
Translate using local space to center Camera on gameObject
Hi All,
I have a camera that is rotated 45 degrees on both the x and y axis, as we are working on an isometric game. That camera is looking at some cubes.
I want to make my camera smooth pan using lerp to whatever direction is required in order to center on a gameObject that is being passed in.
When panning right now, I translate using space.self in order to preserve our isometric rotation. I don't want rotate the camera, as that would break our isometric view, so using the lookat function is out.
I already use panning with right click and dragging that uses space.self, but I haven't been able to apply that to this idea.
I've looked at the question below and believe I must need to create a plane that is also working on the same angle as my camera. http://answers.unity3d.com/questions/400209/moving-camera-to-center-on-clicked-object.html
Here is my current script. It wont be too cohesive at the moment as I've merged a bunch of different scripts to get the functionality I want.
Can someone please point me in the right direction? :)
using UnityEngine;
public class DynoCam : MonoBehaviour
{
public static DynoCam instance;
public float PanSpeed = 1;
public int speed = 3;
private Vector3 dragOrigin;
Vector3 startPos;
public bool cameraDragging = true;
public bool CanDoCameraStuff = true;
float outerLeft = -20f;
float outerRight = 2f;
float outerUp = 45f;
float outerDown = 15f;
float zoomSensitivity = 15.0f;
float zoomSpeed = 2.0f;
float zoomMin = 3.0f;
float zoomMax = 30.0f;
private float zoom;
public Camera selectedCamera;
public float MINSCALE = 2.0F;
public float MAXSCALE = 5.0F;
public float varianceInDistances = 5.0F;
private float touchDelta = 0.0F;
private Vector2 prevDist = new Vector2(0, 0);
private Vector2 curDist = new Vector2(0, 0);
private float startAngleBetweenTouches = 0.0F;
private int vertOrHorzOrientation = 0;
private Vector2 midPoint = new Vector2(0, 0);
void Start()
{
if (instance == null)
instance = this;
else
Destroy(this);
startPos = transform.position;
selectedCamera = Camera.main;
zoom = Camera.main.orthographicSize;
if (Application.platform != RuntimePlatform.Android && Application.platform != RuntimePlatform.IPhonePlayer)
{
zoom = Camera.main.orthographicSize * 1.5f;
}
}
public void ResetCameraPosition()
{
transform.position = startPos;// GetCenteredCameraPos();
Camera.main.orthographicSize = 3;
Camera cam;
Plane[] planes;
cam = Camera.main;
int loopProtection;
foreach (GameObject G in generateLevel.instance.LandMass)
{
loopProtection = 0;
while (true)
{
planes = GeometryUtility.CalculateFrustumPlanes(cam);
//if (!GeometryUtility.TestPlanesAABB(planes, anObjCollider.bounds))
if (!GeometryUtility.TestPlanesAABB(planes, G.GetComponent<GridTile>().HeldBlock.GetComponent<Collider>().bounds))
{
cam.orthographicSize += 0.5f;
}
else
{
break;
}
loopProtection++;
if (loopProtection > 15)
break;
}
}
cam.orthographicSize += 3.5f;
cam.orthographicSize = Mathf.Clamp(cam.orthographicSize, 5, 40);
}
private Vector3 GetCenteredCameraPos()
{
float x = 0f;
float y = 0f;
float z = 0f;
foreach (GameObject pos in generateLevel.instance.LandMass)
{
x += pos.transform.position.x;
y += pos.transform.position.y;
z += pos.transform.position.z;
}
return new Vector3(x / generateLevel.instance.LandMass.Count, y / generateLevel.instance.LandMass.Count, z / generateLevel.instance.LandMass.Count);
}
void Update()
{
if (CanDoCameraStuff)
if (GameManager.instance.CurrentState == GameState.Play)
{
//
// if pc version
//
if (Application.platform != RuntimePlatform.Android && Application.platform != RuntimePlatform.IPhonePlayer)
{
zoom -= Input.GetAxis("Mouse ScrollWheel") * zoomSensitivity;
zoom = Mathf.Clamp(zoom, zoomMin, zoomMax);
}
//
// if mobile version
//
else
{
if (!GodController.instance.ClickedOnTile)
{
//if (Input.touchCount != 0)
//
if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
//Debug.Log(Input.touchCount);
Vector2 touchDeltaPosition = Input.GetTouch(0).deltaPosition;
float panSpeedMobile = 1f * Time.deltaTime;
Vector3 lerpToPos = new Vector3(-touchDeltaPosition.x * panSpeedMobile, -touchDeltaPosition.y * panSpeedMobile, 0);
transform.Translate(-touchDeltaPosition.x * panSpeedMobile, -touchDeltaPosition.y * panSpeedMobile, 0);
//transform.position = Vector3.Lerp(transform.position, transform.position + lerpToPos, Time.deltaTime * panSpeedMobile);
}
else if (Input.touchCount == 2 && Input.GetTouch(0).phase == TouchPhase.Moved && Input.GetTouch(1).phase == TouchPhase.Moved)
{
midPoint = new Vector2(((Input.GetTouch(0).position.x + Input.GetTouch(1).position.x) / 2), ((Input.GetTouch(0).position.y - Input.GetTouch(1).position.y) / 2)); //store midpoint from first touches
curDist = Input.GetTouch(0).position - Input.GetTouch(1).position; //current distance between finger touches
prevDist = ((Input.GetTouch(0).position - Input.GetTouch(0).deltaPosition) - (Input.GetTouch(1).position - Input.GetTouch(1).deltaPosition)); //difference in previous locations using delta positions
touchDelta = curDist.magnitude - prevDist.magnitude;
if ((Input.GetTouch(0).position.x - Input.GetTouch(1).position.x) > (Input.GetTouch(0).position.y - Input.GetTouch(1).position.y))
{
vertOrHorzOrientation = -1;
}
if ((Input.GetTouch(0).position.x - Input.GetTouch(1).position.x) < (Input.GetTouch(0).position.y - Input.GetTouch(1).position.y))
{
vertOrHorzOrientation = 1;
}
if ((touchDelta < 0)) //
{
zoom = Mathf.Clamp(Camera.main.orthographicSize + (1 * speed), zoomMin, zoomMax);
}
if ((touchDelta > 0))
{
zoom = Mathf.Clamp(Camera.main.orthographicSize - (1 * speed), zoomMin, zoomMax);
}
}
Camera.main.orthographicSize = Mathf.Clamp(Camera.main.orthographicSize, zoomMin, zoomMax);
}
}
}
}
void LateUpdate()
{
if (GameManager.instance.CurrentState == GameState.Play)
{
Camera.main.orthographicSize = Mathf.Lerp(Camera.main.orthographicSize, zoom, Time.deltaTime * zoomSpeed);
//
// if pc version
//
if (Application.platform != RuntimePlatform.Android && Application.platform != RuntimePlatform.IPhonePlayer)
{
Vector2 mousePosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
float left = Screen.width * 0.2f;
float right = Screen.width - (Screen.width * 0.2f);
if (mousePosition.x < left)
{
cameraDragging = true;
}
else if (mousePosition.x > right)
{
cameraDragging = true;
}
if (cameraDragging)
{
if (Input.GetMouseButtonDown(1))
{
dragOrigin = Input.mousePosition;
return;
}
if (!Input.GetMouseButton(1)) return;
Vector3 pos = Camera.main.ScreenToViewportPoint(Input.mousePosition - dragOrigin);
Vector3 moveX = new Vector3(pos.x * PanSpeed, 0, 0);
Vector3 moveY = new Vector3(0, pos.y * PanSpeed, 0);
if (moveX.x > 0f)
{
if (this.transform.position.x < outerRight)
{
transform.Translate(moveX, Space.Self);
}
}
else
{
if (this.transform.position.x > outerLeft)
{
transform.Translate(moveX, Space.Self);
}
}
if (moveY.y > 0f)
{
if (this.transform.position.y < outerUp)
{
transform.Translate(moveY, Space.Self);
}
}
else
{
if (this.transform.position.y > outerDown)
{
transform.Translate(moveY, Space.Self);
}
}
}
}
//
// if mobile version
//
else
{
}
}
}
}
Here's a supplementary image. I want to be able to click on any single one of those blocks, and the camera would move over to the right and the block should be in the center of the screen
Answer by YesNoKonrad · Oct 18, 2016 at 01:55 AM
on click, you could send a Raycast from your camera through the middle of the screen onto the object plane ( i assume that every tile of your game exists at the same height? like y = 0 for example? so you would create an invisible plane with a collider and chose raycast as gameobject.layer (has to be created), your raycast uses then the raycast-layermask)
this gives you the position you are currently looking on, now you get the position from the object you clicked on and substract your viewposition from it.
the result is a translation-vector that should do the trick. simply add the vector to your camera position.
Hi @YesNo$$anonymous$$onrad! Thank you for getting back to me.
This is where I managed to get to. Every tile of my game exists on Y level 20. I am guessing at a lot of things with the code below.
Plane plane = new Plane(new Vector3(0, 20, 0), Vector3.zero);
Vector3 v3Center = new Vector3(0.5f, 0.5f, 0.0f);
Ray ray = Camera.main.ViewportPointToRay(v3Center);
float fDist;
if (plane.Raycast(ray, out fDist))
{
Vector3 v3Hit = ray.GetPoint(fDist);
Vector3 v3Delta = SelectedTile.transform.position - v3Hit;
Camera.main.transform.Translate(v3Delta);
}
I wasn't really sure about working with the raycast layer when working with the code above, as from my understanding, plane.raycast only interacts with both the plane and the ray that I've made from the camera?
Regardless, I'm still sure that I need a plane that is made from my camera viewport somehow, but I don't know if that is true. Currently, with the above, clicking on a block will send the camera very far up and to the left, even if I had the camera centered on the tile.
You misunderstood the constructor of the plane.
The first vector describes the normal of the plane and the second the distance from center as a point. If you are familiar with math then you can recognize that that is one of three methods to define a plane: You define a point in space that lies on your plane, and the inclination of the plane via the normal
So your plane is placed at 0,0 and if the costructor does not normalize the vector that should be normal (length 1) then it is also strechted by 20.
it should look like this
Plane plane = new Plane(new Vector3(0, 1, 0), new Vector3(0, 20, 0));
Edit 2: You can ignore the stuff with the raycast-mask, that was for a finite plane, id est a simple object with a mesh. the solution you found is way more elegant.
Edit: Tipp: Whenever you use a class that you never used before, look into it in the scripting section of the documentation. It's invalueable. :)
Answer by Zodiarc · Oct 18, 2016 at 07:03 AM
You can make the camera a child of an empty game object (let's call it beam). This beam should always stay at ground level. Then you can lerp the position of the beam to your wanted position and the camera will follow while keeping its rotation (but make sure the camera is actually looking at the beam). I'm using the same technique here: https://www.youtube.com/watch?v=b6H_9M8Va6w
+1 For avoiding raycasts, which are expensive. Simple but effective. I really like it.
@YesNo$$anonymous$$onrad actually it uses one raycast so that the empty parent always stays on top of the terrain and other props like buildings. Baiscally it's a simple drag & drop rts/tbs camera solution which works out of the box. But in this case the raycast isn't really needed.
Your answer
Follow this Question
Related Questions
Move camera along 2 axes in WorldSpace 2 Answers
Center Object in Viewport 1 Answer
Isometric game, Camera movement breaks when fps drops. 1 Answer
Moving a Rotated Camera 0 Answers
Why are my controls laggy 1 Answer