I’m working on an RTS camera and I need to have it so that I can focus on individual units at the press of a button. However, I’m dealing with a camera with an X rotation of 60 degrees, so I can’t just dump the camera on top of the unit’s X/Z position. Right now I’m thinking that I should use construct a plane, cast a ray at it from the Camera’s forward position and then calculate the X/Z offset from there, but surely there’s a more elegant solution than this?
Any help would be great, thanks.
edit: Casting a ray to the plane definitely works, but it’s functionally inelegant for a lot of reasons. It also starts to panic if the target isn’t on a plane equal to the one I’ve created.
I would probably have either a hard-coded offset, or perhaps a customizable offset on a per-unit basis, and when you are zooming on the object, then you will “tween in” the camera from its high position to the “look at me” position, then when the focus is released, you would zoom back to the high view again.
Easiest way is to have a script on the zoomable object that contains what its offsets should be, and then your zoom script would look for such a component with .GetComponent() (or whatever), and if it doesn’t have that, would use a default prefix.
This way you can have both a camera position for the zoomed view, as well as a “what do I look at?” point for the unit. That lets you frame the camera views nicely for the unit.
The problem with a hard-coded camera offset is that it makes it so that when units are at a different elevation, they don’t line up with the centre of the screen when focused on. I’ll give an approximation since I just woke up and I don’t feel like commenting out the code that currently works.
Here we have the camera focused on the unit with the yellow circle around it. As you can see, the camera is at an angle, but the unit is still focused on. The unit shares the same X/Z plane as the unit on the platform, which has an elevated Z position.
Here we have the camera panned over to what the focus position would be if I hard-coded the camera offset to focus on the unit on the platform. As you can see, the higher/lower the unit gets, the further away from the centre of the screen the unit gets. As I’m planning to have elevated areas, this is a problem, as the player would have to manually move the camera to get them at the centre of the screen.
Here we have the unit properly focused using the system where I have a plane constructed and am casting a ray toward that plane. This works but the code is inelegant. It’s like using a bunch of if statements when a for loop will do.
I simply want to know if there’s a method that will allow me to do this that doesn’t rely on constant raycasts.
I just realized my proposed solution is what you’ve described at the end of your post.
I’ll leave it here for your consideration:
// create a plane facing up, sitting at the position of the camera
Plane plane = new Plane(Vector3.up, Camera.main.transform.position);
Ray ray = new Ray(target.transform.position, -Camera.main.transform.forward);
float distance;
if(plane.Raycast(ray, out distance))
{
Camera.main.transform.position = ray.GetPoint(distance);
}
It doesn’t require constant raycasting, just once when you swap targets, then you can maintain the offset unless your unit changes elevation. Even if you did a raycast every frame (or some larger interval) for this one unit you’re following, it shouldn’t be a problem performance-wise, and the code is sufficiently elegant in my opinion.
The issue is that the camera is at a fixed altitude, so a hard-coded offset would have to change the camera’s altitude along with the unit, which is not desired. Instead, OP wants the camera to remain at the same altitude but offset on the XZ plane to account for the off-center screen position of the unit.