How do I rotate my Camera similar to Tekken/Soul Calibur in C#

Godot Version

Tried in 3.6 and 4.2.2

Question

For months I’ve been struggling trying to create a 3D Camera system that works like either Tekken or Soul Calibur. My main issue to getting the camera to rotate around the camera’s pivot point while still keeping both players within the camera. Tried getting a reference to difference of both player’s positions to use that as an offset and used a Vector3.Cross to get perpendicular to camera’s pivot point but that didn’t work. I tried using a LookAt function to focus on the Y position of the Camera Pivot, that didn’t work. I tried using RotateY and other methods and that didn’t work. Can someone explain what am I doing wrong? Cause when I write something similar in Unity I was able to get a result that I wanted. Thanks for the help.

For reference here is some comment snippets of the function I was trying to work on.

public void RotateCamera() { //Allows the Camera to rotate around both players.

//Vector3 offset = targets[0].Position - targets[1].Position;
//Vector3 crossPoint = Vector3Extensions.Cross(offset,Vector3.Up);
//LookAt(crossPoint,Vector3.Up,true);
//LookAt(CameraPovit.Position);
//CameraPovit.Rotation = Vector3Extensions.LookRotation(crossPoint,Vector3.Up);
//var ModelHolder = GetTree().Root.GetNode(“TestArena/Fighter/ModelHolder”);

//CameraPovit.Rotation = ModelHolder.Rotation;
//CameraPovit.RotateY(ModelHolder.Rotation.Y);
//LookAt(new Vector3(0,ModelHolder.Position.Y, 0),Vector3.Up);
//GD.Print(“Camera” + CameraPovit.Rotation);
//GD.Print(“Player A” + ModelHolder.Rotation);
}

This would be my approach (untested):

  • start with a typical third person controller. Usually I’d use a pivot to rotate the camera around (as you did as well)

hierarchy example:

Level
→ Player1
→ Player2
→ CameraPivot
→ → Camera

  • place the camera pivot right between the two players, and set the camera distance by changing the Position of the camera (not the pivot)

  • have a reference to both players in the script that controls the camera

[Export] private Camera3D camera;
[Export] private Node3D cameraPivot;
[Export] private Player player1;
[Export] private Player player2;
  • everytime you want to change the camera position, set the x and z coordinates of the camera to the point between both players
public void UpdateCameraCenter()
{
	float centerX = (player1.GlobalPosition.X + player2.GlobalPosition.X) / 2;
	float centerZ = (player1.GlobalPosition.Z + player2.GlobalPosition.Z) / 2;
	cameraPivot.GlobalPosition = new Vector3(centerX, cameraPivot.GlobalPosition.Y, centerZ);
}
  • for rotating around the player you might want to use something like this
public void SetCameraRotation(float newRotationAngleRad)
{
	cameraPivot.Rotation = Vector3.Zero;
	cameraPivot.RotateY(newRotationAngleRad);
}
  • this should work until the distance between the players changes. You probably want to have the camera zoom/distance in a way to always keep both characters visible.
    For this you might want to check out the kidscancode tutorial about multitarget cameras

When I’m using the function RotateY, is that supposed to automatically add the rotation when one of my players move? Cause the newRotationAngleRad only changes once and not dynamically. I’m calling this function in the _Process function if that helps.

Edit: I’ve been experimenting with the Angle and using my player’s position for the angle is getting me close but it’s not what I’m looking for.

The code I’ve described can be used if you always pass the actual rotation (not a rotation change).

So yes, RotateY rotates the camera relative to the current position.

But this line:

cameraPivot.Rotation = Vector3.Zero;

resets the rotation.

If you want to constantly rotate the camera by the same amount, remove that line.
So if you want to rotate by a fixed amout, e.g. 10° per second, you can do something like this:

public override void _Process(double delta)
{
	ChangeCameraRotation((float)(Mathf.DegToRad(10) * delta))
}

public void ChangeCameraRotation(float rotationAngleRadChange)
{
	cameraPivot.RotateY(rotationAngleRadChange);
}

From how this is being written logically, it should works since it’s just modifying the Y-axis’s rotation over time but it still doesn’t work for some reason. Here is a video demo of what I’m seeing.

Video Demo

so what happens if you remove line 58?

The camera will continually spin on the pivot but not on command when one of the players move inwards or outwards.

I might have misunderstood if you want the camera to spin around constantly or not.

So what you actually want is probably to align the camera so that it is always facing the sides of both players. Because you already do a look_at on both players so that they face each other, I think you only need to find out the current player rotation of one player (around y) and rotate it by 90 degree.
Since we are passing a target rotation instead of a relative change, lets use this snippet for setting the rotation:

Now in _Process() you do something like this:

float rotationOffsetRad = Mathf.Pi / 2; // 90° in Radians
float rotationRad = player1.Rotation.Y + rotationOffsetRad;
SetCameraRotation(rotationRad)

If the angle is not correct yet, you may need to test other values for rotationOffsetRad, e.g. -Mathf.Pi / 2

There’s a Unity-Cinemachine-like plugin named phantom-camera. You can refer to the group follow mode logic in the plugin or just use the plugin in your project. It’s written in GDScript but interop between C# and GDScript is simple enough.

The source of group follow mode:

I find your post hilariously ironic cause my original solution was Phantom Camera and it wasn’t working right so I started from scratch :rofl: