So here is what Im trying to achieve, seems like it would be simple but the solution alluding me. I’m working on a standard little Tower Defense style game. When I click on an object in my game, I want the camera to move so it is centered in the screen. I started working on it the realize I couldn’t just lerp the cameras current x and z position to the selected game ojbect x and z position because the camera is pointing down at an angle, and that would position the camera directly above the object and it would not end up in the center of the screen.
So…any pointers on how to achieve my goal? Giving it some thought what came to mind was get the objects vector 2, then lerp that towards screen.hieght/2 and screen.width/2. But being a new programmer I do not know if Im barking up the right tree there.
OK, this shouldn’t be too hard… you have a certain angle that the camera makes with the ground. Think of this as a Vector3 that points from the point on the ground, the one right in the center of the screen, to the camera. In fact if it were me, I would probably compute this vector on Start, so that you can tweak the camera position/angle in the scene editor, and have it “stick” when you run the game. Then you can simply add this offset to whatever ground point you want to center, and that’s the new camera target position.
To illustrate, here’s a script that centers whatever position you click on. (This seems pretty handy — I think this is going it end up it my library of standard scripts!)
using UnityEngine;
public class CamTest : MonoBehaviour {
Vector3 groundCamOffset;
Vector3 camTarget;
Vector3 camSmoothDampV;
private Vector3 GetWorldPosAtViewportPoint(float vx, float vy) {
Ray worldRay = camera.ViewportPointToRay(new Vector3(vx, vy, 0));
Plane groundPlane = new Plane(Vector3.up, Vector3.zero);
float distanceToGround;
groundPlane.Raycast(worldRay, out distanceToGround);
Debug.Log("distance to ground:" + distanceToGround);
return worldRay.GetPoint(distanceToGround);
}
void Start() {
Vector3 groundPos = GetWorldPosAtViewportPoint(0.5f, 0.5f);
Debug.Log("groundPos: " + groundPos);
groundCamOffset = camera.transform.position - groundPos;
camTarget = camera.transform.position;
}
void Update() {
if (Input.GetMouseButtonDown(0)) {
// Center whatever position is clicked
float mouseX = Input.mousePosition.x / camera.pixelWidth;
float mouseY = Input.mousePosition.y / camera.pixelHeight;
Vector3 clickPt = GetWorldPosAtViewportPoint(mouseX, mouseY);
camTarget = clickPt + groundCamOffset;
}
// Move the camera smoothly to the target position
camera.transform.position = Vector3.SmoothDamp(
camera.transform.position, camTarget, ref camSmoothDampV, 0.5f);
}
}
Read it over twice and I think I got the gist of it. Im done working on this for the day so Ill give it try in the morning and let you know how it goes.
Just tested it out and it works almost exactly how I wanted it to. When I click, the camera quickly zooms over to that point I hit. Only problem is that it seems to conflict a little with my camera control script I already have working in the game. I use the scroll wheel to zoom in and out, and that function doesnt seem to work with this script attached. Also the camera is kinda locked to the point you click on, when you try to pan away the camera moves a bit, then snaps back to the last point you clicked on. Im sure a little tweeking will get the two scripts working together nicely.
It would probably be easier to make one script that does it all (for the camera), rather than try to get the two to work together.
For example, you could handle the scroll wheel in the above script by simply scaling the groundCamOffset vector. Increase it (e.g. multiply by something > 1) to move the camera further away from the ground, and decrease it (multiply by something less than 1) to move it in.
Similarly, if you have some other way to pan the camera, then you should do this by picking a different point in the world. I’d add one more property to the above, perhaps called “pointOfInterest,” which is the point on the ground the camera is looking at — and in fact, you could then eliminate camTarget, since you can easily calculate that on the fly as pointOfInterest + groundCamOffset.
Then life is easy — pan by changing pointOfInterest; zoom by scaling groundCamOffset; and let the Update method of this script move the camera accordingly.
Thanks, Ill give that a shot. Im at a point where Im trying really hard to keep my scripts neatly organized, and merging this with my existing cam system will be perfect. Mostly because I currently have one script to handle mouse input, one to handle keyboard input, then one to handle all GUI updates.
Doing it this way fits right into my wheelhouse anyhow. I find it easier for me when adding functionality to write it in a separate script and make sure its working as intended, then add it into my existing uber-scripts.
This is what i do to center my camera either onto an objects pivot or hit.point center.
using UnityEngine;
public class CameraCenterOnHitPoint : MonoBehaviour
{
public bool PivotCenter = false;
public float Smooth = 50f;
private Quaternion _targetRotation;
void Update()
{
// Define keycode and mouse press down
if (Input.GetKey(KeyCode.LeftShift) && Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit = new RaycastHit();
if (Physics.Raycast(ray, out hit, Mathf.Infinity))
{
// Find the vector pointing from our position to the target
Vector3 _direction = ((PivotCenter ? hit.transform.position : hit.point) - transform.position).normalized;
// Create the rotation to look at the target
_targetRotation = Quaternion.LookRotation(_direction);
}
}
transform.rotation = Quaternion.RotateTowards(transform.rotation, _targetRotation, Smooth * Time.deltaTime);
}
private void OnGUI()
{
Rect rect = new Rect(Screen.width / 2 - 50, Screen.height / 2 - 50, 100, 100);
GUI.Box(rect, "Center");
}
}