How to do RuneScape camera movements w/ arrow keys?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By throwaway47

Camera rotation in third-person is not a problem, it just comes down to a simple .look_at(player), and moving the camera around in a first-person shooter is easy as you simply steal the player’s coordinates. However, camera movement in a third person game is tricky.

For example, in RuneScape, you are in third-person, and you can move your camera around your player using arrow keys. For instance, if you hold the left arrow key, your player will stay in the dead center, but your camera view will be circling around your player in a clockwise manner. The left/right keys can always move in a full circle around your player. In this process, the x and z values change but the y value remains fixed.

If you use the up/down keys, this same circular movement will happen, but in the up/down direction, in that if your player was facing the camera view, your view would circle around the player’s front and back, going under surface each time in the process. Of course, this would only happen if the up/down movement is unrestricted, which is not the case in RuneScape. This would likely be a simple min() and max() to prevent the camera from going beneath the surface, or too high up. This type of movement will alter x, y and z values.

This type of system can be thought of as a sphere of available camera positions, where the camera is a point traveling along the edge of the sphere, and the center point of the sphere is the player’s position. The radius of the sphere is the distance between the player and the camera. Of course, one could do all the spherical math like this Godot user did to determine how to move the camera around, but it’s very messy.

Surely there is a much better way to do this with Godot transforms? The answer by MysterGM to the above question alludes to the use of transforms to solve this problem, but doesn’t provide any examples.

In summary: how do you use Godot transforms to move your camera like in RuneScape?

:bust_in_silhouette: Reply From: Coxcopi

I think the easiest way would be to have a Spatial node as the Pivot, or in your words the center of the sphere, and then put it at the position of the players head. Then just parent the camera to the Pivot Node, and the distance between the Pivot and your Camera is like the radius of the sphere.

Now just make the Pivot rotate on the y axis for the left and right arrow keys, and make it rotate on the x or z axis (depends how your player is facing) for the up and down keys. (That’s where you would need to add the restrictions)

That should give you the exact camera ‘movement’ (since it’s actually only the rotation of the pivot) that you wanted.

I’ve essentially done this, and used the basic trigonometry to rotate, but it only works for left/right rotation. In such a rotation, the y of the camera never changes. You stay at the same height while you circle around the player, which means both the x or z will change, but not the y.

For up/down rotation, let’s say the camera is currently at (5, 5, 5), and your player wants to “go up,” that is, increase the y. In order to do this while facing the character in the same direction, both the x and z need to decrease, while your y increases. As your view becomes more of a bird’s eye view, you are both getting higher up, while having a more “directly above” view. The end result of such a rotation could be something like (2, 8, 2). You are now higher, but also looking at the top of the player’s head.

As all three dimensions are subject to change from just a single direction of a rotation, you need to work in 3D space, as you are moving inside a sphere in the end. I’ve found the following workaround, but it seems bad:

func up_down_rotate(rad):
	var focal_point = $Player.transform.origin
	var cam_pos = $Camera.transform.origin
	var relative_cam_pos = cam_pos - focal_point

	var xz_theta = atan2(relative_cam_pos.z, relative_cam_pos.x)

	# Clear the current xz angle.
	rotate_2d(-xz_theta, "x", "z")

	# Rotate in the up/down space.
	rotate_2d(rad, "x", "y")

	# Revert the xz angle reset.
	rotate_2d(xz_theta, "x", "z")

Basically, I’ve realized that multiple 2D rotations on different dimensions amounts to a 3D rotation. The rotate_2d function basically just finds the hypotenuse and does the trigonometry. However, I’ve noticed I cannot properly rotate along the xy dimensions unless my xz angle is 0. So I change that to zero, move “up” in my camera view, and then I revert my angle reset.

Is there a better way to do this?

throwaway47 | 2020-08-17 13:44