Camera system - X-Hax/SA2BModdingGuide GitHub Wiki

The SA2 camera system is an improvement of the SA1 one, but the idea is the same.

Basically, there is a main camera task that runs subtasks which in turn make the camera move. Only one subtask can be active at once, but they can be stacked. In the case of overlapping cameras in the camera layout, it will stack add and remove them in order. The system supports 16 subtasks.

Each subtask is made of:

  • Mode: the main logic function the calculates position, angle, etc
  • Adjust: a post process function to adjust the calculated stuff (to smoothly transition between cameras for example)

The idea is that one camera can use any combination of a mode and an adjust. In the camera layout file, the user can specify the mode and the adjust they want for the camera entry, as well as a trigger boundary.

See the CameraModes and CameraAdjust enums in the Mod Loader includes for a list of their official names.

Camera Layout

Camera in code

Internally, the game stores 4 camera data even though it only officially supports 2 players. The main struct is called CameraInfo in the Mod Loader includes and it's used for CameraData[pnum] at 0x1DCFF40. It also contains the field of view and shakiness information of the camera.

Useful functions

  • You can retrieve the current subtask id ("slot") with GetCurrentCameraSlot(int pnum)
  • You can retrieve the previous slot with GetPreviousCameraSlot(int pnum) (basically GetCurrentCameraSlot(pnum) - 1.)
  • You can retrieve the camera mode with GetCameraMode(int pnum, int slot), see CameraModes enum.
  • You can add a camera into a slot with SetCameraMode(int pnum, int slot, CameraModes mode).
  • You can add a camera into the next free slot with RegisterCameraMode(int pnum, CameraModes mode).
  • You can add a camera with a custom function into a slot with SetEventCameraFunc(int pnum, int slot, CameraFuncPtr func).
  • You can add a camera with a custom function into the next free slot with RegisterEventCameraFunc(int pnum, CameraFuncPtr func).
  • You can set the adjust of a camera slot with SetAdjustMode(int pnum, int slot, CameraAdjusts adjust).
  • You can remove a camera with ReleaseCamera(int pnum, int slot), it will then restore the previous camera if there is any.

Note 1: adding a camera also sets it as the current one, you can force the current one with CameraData[pnum].currentCameraSlot = x, but keep in mind the game expect currentCameraSlot - 1 to be the previous camera.

Note 2: the "register" functions simply add a camera into currentCameraSlot + 1, for example the implementation of RegisterCameraMode is simply SetCameraMode(pnum, CameraData[pnum].currentCameraSlot + 1, mode);

Coding a custom camera

A Camera Mode is a function with a CameraInfo and a CameraParam pointer. The CameraInfo is the main camera data for the current camera, only edit it directly if you want to change field of view, shakiness, or target mode (more on that later.) The CameraParam is information specificly for the camera mode, it contains a CameraWork pointer which is 256 free bytes for you, avoid editing the rest.

Let's start with a basic template:

void __cdecl MyCustomCamera(CameraInfo* cam, CameraParam* param)
{
	if (param->ulTimer == 0)
	{
		// eventual initialization code
	}

	// logic
}

RegisterEventCameraFunc(playerid, MyCustomCamera); <- to add a custom camera
SetAdjustMode(playerid, GetCurrentCameraSlot(), CameraAdjust_None); <- optional, removes adjust

Upon calling CameraSetEventCameraFunc, the game will add a camera with the "user" mode and your own custom function. You could also replace an existing camera with CameraMode[CameraMode_Klamath] = MyCustomCamera.

To edit the camera location in custom function, you should not edit cam directly, but use the following global variables:

  • CameraPos: The position of the camera in world space.
  • CameraAng: The angle of the camera in binary format (0-0x10000).
  • CameraTgt: The position at which the camera looks at.
  • CameraDir: The direction of the camera, it's a direction vector so { 0.0f, 1.0f, 0.0f } is up.
  • CameraSpeed: The distance with the previous position basically.

Which you need to set depends on CameraTargetMode at 0x1DCFF00 0. Turns the camera toward CameraTgt, you only need to set CameraPos and CameraTgt

  1. Turns the camera toward CameraAng, you only need to set CameraPos and CameraAng
  2. Use CameraDir, you only need to set CameraPos and CameraDir
  3. Orbital camera mode, with CameraDir * CameraSpeed as the distance from CameraTgt, you only need to set CameraTgt, CameraDir and CameraSpeed
  4. Orbital camera mode, with CameraDir as the distance from CameraTgt, you only need to set CameraTgt and CameraSpeed

With that in mind you can create a camera that looks at a point:

void __cdecl MyCustomCamera(CameraInfo* cam, CameraParam* param)
{
	CameraTargetMode = 0;                              // Look at CameraTgt
	CameraPos = MainCharObj1[CurrentScreen]->Position; // Place camera at player position
	CameraTgt = { 0, 0, 0 };                           // Look at point 0, 0, 0
}

You can change the field of view by using this:

cam->fov_target = [new field of view value in binary angle, default is 0x31C7];
cam-fov_spd = [how fast it should reach the target fov in binary angle];
// or cam->fov = [value]; if you don't want a transition.

You can make the camera shake with:

cam->shake_magnitude = 1.0f; // strength
cam->shake_timer = 60;       // for how long
cam->shake_mode = 1;         // enable (don't spam it)

You can specifiy if you want your camera to collide with the environment:

boolCameraCollision = TRUE/FALSE