- Home /
Fixed Camera Scene a la FF7 or Resident Evil style
I've been putting together an RPG reminiscent of the Final Fantasy games but I'm having a very difficult time trying to get a fixed camera to work correctly.
The Black Tower game (unfortunately no longer in production) was built in Unity and has the exact kind of Camera setup I want: http://www.youtube.com/watch?feature=player_embedded&v=RByznMHg_L0#t=98
It shows walking around in the town around 1:38 time mark.
I have my fixed camera using LookAt for the player but I can't figure out how to stop it from revealing the end of the map. In that video, the camera follows the player but stops once it reaches the end of the scene and the player continues on.
If anyone can point me in the right direction, it would be much appreciated.
Answer by Tomer-Barkan · Dec 04, 2013 at 05:53 AM
If you want to keep the Y and Z rotation constant (and zero I guess), so the 'horizion' appears always as a straight line, and it's always facing say north, you're going to have to move the camera around with the player, at least in the X and Z planes, while keeping it's Y coordinates (height) constant, to create a constant 60 degrees diagonal view.
So given the player's position (this example makes the camera look from south to north (from Vector3.back to Vector3.forward), in a 60 degree angle:
void SetCameraPosition(Transform camera, Transform player, float distanceFromPlayer) {
Quaternion rotate60Upwards = Quaternion.AngleAxis(60, Vector3.right);
Vector3 fromPlayerToCamera = (rotate60Upwards * Vector3.back) * distanceFromPlayer;
camera.position = player.position + fromPlayerToCamera;
camera.rotation = Quaternion.AngleAxis(60, Vector3.right);
}
Now, you can either move the camera the exact same amount that you move the player, whenever the player moves (without rotating the camera any further), or you can simply run the code above every frame (in Update()
) so the camera is always updated according to the player location.
Edit: I now understand that you want the camera to stay fixed and rotate, but limit the rotation. What you can do is calculate the most extreme rays of the camera (the corners of the screen), and check that none of them are outside your boundaries. You can do that by placing a huge box collider behind your scene in the area, but only in the area that you want to be visible, so that when there is a point that you don't want to be visible, the collider will not be behind it. Set a specific layer for this collider, say "AreaLayer". Then cast the extreme rays with the AreaLayer as a mask, and see if they hit the collider. If they do not, don't allow the camera to rotate any further:
void FollowPlayer(Transform player) {
Quaternion previousRotation = transform.rotation; // remember original rotation
transform.LookAt(player, Vector3.up); // look at player
// if looking at player caused the camera to turn outside its bounds, restore previous rotation
if (IsCameraOutOfBounds()) {
transform.rotation = previousRotation;
}
}
bool IsCameraOutOfBounds() {
Ray[] edgeRays = GetCameraEdgeRays(); // get camera extreme rays
int layerMask = 1 << LayerMask.NameToLayer("AreaLayer"); // raycast layer should only find the "AreaLayer" collider - the collider that depicts the game area
foreach (Ray ray in edgeRays) {
// if raycast doesn't hit the area layer collider, camera is out of bounds
if (!Physics.Raycast(ray, Mathf.Infinity, layerMask) {
return true;
}
}
return false;
}
// get 4 rays, one for each corner of the camera's view
Ray[] GetCameraEdgeRays() {
Ray[] rays = Ray[4];
ray[0] = camera.ScreenPointToRay(0, 0);
ray[1] = camera.ScreenPointToRay(0, Screen.height);
ray[2] = camera.ScreenPointToRay(Screen.Width, 0);
ray[3] = camera.ScreenPointToRay(Screen.Width, Screen.height);
return rays;
}
I appreciate the input but I'm really hoping to use a camera that doesn't change its transform to follow the player ins$$anonymous$$d uses LookAt to follow. I'm having a problem with keeping the camera from seeing the end of the map.
In the video, the camera stays in one position and uses LookAt to follow the player. As the player reaches an edge of the map, the camera reaches a boundary and stops following the player. That's what I'm trying to do.
Sorry, it's been several days of overtime and bits of free time to mess with this so answer accepted.
Unfortunately, as well as this code performs it's setup in a way that if you take the camera out of AreaLayer, it will lock the camera. Then, if you move around to another position where you are back in the AreaLayer, the camera will jump to that position. It doesn't seem possible to do a fixed camera without hardcoding clamping boundaries so it slides along the sides of the area.
I've tried so many different ways but to no avail. Just seems I'll have to just put in the boundary floats for top, bottom, left and right on each scene. I was hoping to make my camera script automatically figure out the boundaries and move the camera accordingly no matter what Area I have in the scene.
Thank you @tbkn, definitely a learning experience!
Answer by Womrwood · Dec 05, 2013 at 01:59 AM
You could establish a limit angle of rotation on the Y and X axis.
Store the the orientation of the camera at the center of the scene in a variable called middleOrientation. Every frame get a new orientation looking directly to the target player called targetOrientation. Now test the angle between the X axis of the middleOrientation with the X axis of the targetOrientation and check if it exceeds the limit angle rotation about that angle if not continue otherwise you will have to undo the orientation with exceedAngle - limitAngle. Do the same thing with the Y axis.
To get the angle between 2 axis you can use the following code:
var vec1=Vector3(2,3,4);
var vec2= Vector3(1,-2,3);
//Get the dot product
var dot:float = Vector3.Dot(vec1,vec2);
// Divide the dot by the product of the magnitudes of the vectors
dot = dot/(vec1.magnitude*vec2.magnitude);
//Get the arc cosin of the angle, you now have your angle in radians
var acos = Mathf.Acos(dot);
//Multiply by 180/Mathf.PI to convert to degrees
var angle = acos*180/Mathf.PI;
//Congrats, you made it really hard on yourself.
print(angle);
I messed up the angles when rotation on the Y axis you can test the 2 forward vectors and when rotation on the X axis you can test the 2 up vectors.
@Womrwood you can get angle between two vectors using Vector3.Angle:
var angle = Vector3.Angle(vec1, vec2);
Thanks, I am new to Unity^^ but a little math knowledge is good too.
lol, not just good, but essential... Especially geometry.
Your answer
Follow this Question
Related Questions
Attaching camera to instantiated object 1 Answer
Changing The Camera Position & Offsetting LookAt 2 Answers
CameraZoom Changeing Position 0 Answers
Lock look rotation first person. 0 Answers