- Home /
Isometric camera view constraints
Advice and best practices in defining camera movement based on screen edge relative world in an isometric environment?
Rudimentary diagram not to scale. Red box is view during runtime. Green is playable area. The goal is to have free movement of the camera until the edge of the view reaches the extent of the playable area and then deny further movement. I know this is simple in a world where the camera doesn't have obscene angles and everything is aligned to world space using a simple Mathf.Clamp, but obviously things are a little different in this world.
Edit This is a fixed rotation camera (30,45,0) and here is the movement script.
using UnityEngine;
public class DragCamera : MonoBehaviour
{
public float dragSpeed = 25;
void Start()
{
transform.rotation = Quaternion.Euler(30, 45, 0);
transform.position = new Vector3 (28, 10, 28);
}
void Update()
{
if (Input.GetMouseButton(0))
{
var h = -(Input.GetAxis("Mouse X"));
var v = -(Input.GetAxis("Mouse Y"));
Vector3 move = new Vector3(h, 0, v) * Time.deltaTime * dragSpeed;
transform.Translate (move);
}
transform.position = new Vector3 (transform.position.x,
10,
transform.position.z);
}
}
Can the camera be rotated or is the rotation fixed? And how are you moving your camera?
I have edited the original question to include the requested information.
One solution is to WorldToViewportPoint() and check the values. First you need to get the positions of the four corners of your plane. You can do that by:
Calculate the Vector3 by hand
Calculate them from the position and size of the field
$$anonymous$$ove a empty game object around the plane to the four corners and record the position in the editor and record the positions
Position empty game objects at the four corners and drag and drop them into variables in the script.
Position empty game object at the four corners, give the four the same, unique tag, and use GameObject.FindGameObjectsWithTag() to find the four positions.
Then each frame:
Save the current position of the camera
Do the movement
For all four points positions, convert them using Camera.WordToViewportPoint() to get the Viewport coordinate of each point.
If any point has an x or y value that is in the range of 0.0 to 1.0 restore the camera to the saved position. If they are all good, let the movement stand.
Actually it would be easiest and most efficient to have all four points in an array and convert and test each point, quitting when any x or y value is out of the range of 0 to 1.
After giving this somewhat complex solution. I realized you can do something simple. You can just move the camera to the four maximum/$$anonymous$$imum positions the editor and record the $$anonymous$$ and max values for x and y. Then you can do:
Vector3 pos = transform.position;
pos.x = $$anonymous$$athf.Clamp($$anonymous$$X, maxX);
pos.y = $$anonymous$$athf.Clamp($$anonymous$$Y, maxY);
transform.position = pos;
There is no reason to go through the ScreenToWorldPoint(). The ScreenToWorldPoint() is a solution if the angle of the camera changes during the game.
Okay, so maybe my brain is just fried, but I built a test scene and created an array of 4 locations to pass into the code you posted as an example Then threw a DebugLog() into Update so I could see if it was functional and no matter where the camera is positioned, CheckPoints() returns false.
Is there some chance that there is a typo that I'm simply not seeing? Or some other factor that I'm missing?
The only changes to the code above are the declaration of the array, a line of code in Start() that defines the 4 locations manually, and the DebugLog(), all of which appears to be functioning as expected. But looking at your code here, there doesn't appear to be any errors either. If I comment out the Start() line that fills the array, CheckPoints() returns true as expected.
Edit A quick
Debug.Log(v.x);
Debug.Log(v.y);
returns values of below 0 and above 1 right from the start. This may prove useful information to help sort this issue.
Edit2 The "v.y" value doesn't actually change as the camera moves around, it's actually "v.z" which confuses me a bit. That said, the values are as follows at start-up:
v.x = 7.095924
v.y = -1.258339
v.z = 39.71173
As I said a second ago, the x and y values will move when the camera changes positions. But these numbers are telling me that it isn't doing something right.
I'll try your second suggestion to see if I can obtain desired results.
Alright, so I tried the $$anonymous$$athf.Clamp and it works just fine, like could be expected. The problem is, this tracks the camera itself and not the edge of the scree, which is not what I wanted. Is there some way to easily achieve the same result but by using the screen edge that I'm not thinking of?
Answer by robertbu · Aug 17, 2014 at 07:02 AM
I really mess up my answer this time. Usually I'm better than this. I converted my old 'answer' into a comment and am starting over. To start, the original idea (and code) should have used Camera.WorldToViewportPoint(). Viewport coordinates start at (0,0) in the lower left corner of the screen and go to (1,1) in the upper right. So if you get a value outside that range, you know it is off the screen. Looking at your drawing, what you want to prevent is showing a corner. So the logic is the same: save the position, check to see if any of the corners can be seen by the camera, if so, restore the original position, if not allow the move. Here is a rewrite of the check points code:
private Rect rect = new Rect(0.0f, 0.0f, 1.0f, 1.0f);
bool CornersOaky() {
for (int i = 0; i < checkPoints.Length; i++) {
Vector3 pos = Camera.main.WorldToViewportPoint(cornerPoint[i]);
if (rect.Contains(pos))
return false;
}
return true;
}
Yeah I sorted out that it should have been ViewportPoint rather than ScreenPoint, and that it should have been WorldTo, however my values still turn up a range between -1 and 1. I'm guessing that somehow I'm feeding the vectors into the array in the wrong manner, and though I've tried numerous changes over the last while and read countless more references and examples elsewhere I can't seem to sort it. What I want to prevent is the top edge from passing the top corner, the side edges from passing the side corners respectively, and the bottom edge from crossing the bottom corner in each of the respective movement directions.
I'll try this version out, and edit back the outcome.
Edit Okay so the new corner check taking the values from the previously written array works beautifully, now if you can provide a bit of insight as to mix this new bool with the original movement code and I think I cam mark this headache off as an accepted answer.
As the first line in Update():
Vector3 savePos = transform.position;
As the last line in Update():
if (!CornersOaky())
transform.position = savePos;
Appreciate it, will check this out in a second.
New problem though, the bool only checks if the rect contains one of those 4 points. So if the actual corner of the map doesn't show on screen, the camera is permitted to go on indefinitely without registering the bool as false, so it wont matter if I can lock camera movement using the existing code and what you just provided.
Is there no viable way to check the edge of the rectangle or screen/viewport against the locations of the corners even when the corners are not visible?
Edit The Update code you just provided does in fact prevent the viewport edge from crossing the corner, however it's a bit sticky. Once the viewport registers a corner, I would like to prevent further motion in the direction of that respective corner, but allow for free movement on the other axis. As it is now, if I approach, for example, the right corner and the motion stops, the only way to obtain up and down movement is to pull back off of the corner to the left a little bit. $$anonymous$$gestions for fixing this?
With things like this you often have to play with them to see what can be done. The things I can think of are either hacky or a bit ugly to implement. You could rewrite CornersOkay() so that it returns three values: Okay, Horizontal issue, Vertical issue. The value would be based on which of the four points triggered the not okay state. Then you could restore only the x or y based on the return value.
I'm just going to mark this as solved, though it's far from complete and a bit rudimentary, it's still a step in the right direction. I think I'll be able to sort it out after a bit (what's likely to be a few hours) of digging. Thanks robertbu.
If anyone else happens upon this with additional insight or alternative approaches, I'm open to suggestions.
Your answer
Follow this Question
Related Questions
How to make a 3D camera rotate relative to the world? 1 Answer
Help re-calculating camera's viewport on UI drag (when the required area changes)? 1 Answer
I need to find a way to pan my camera around an object. 1 Answer
Isometric Prespective camera drag/drop 0 Answers
Isometric game camera limits 1 Answer