Gamasutra is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.

Gamasutra: The Art & Business of Making Gamesspacer
View All     RSS
October 22, 2020
arrowPress Releases

If you enjoy reading this site, you might also want to check out these UBM Tech sites:


Utilizing 3D Cameras in 2D Games

by GameMaker Studio on 01/31/20 10:25:00 am   Featured Blogs

2 comments Share on Twitter    RSS

The following blog post, unless otherwise noted, was written by a member of Gamasutra’s community.
The thoughts and opinions expressed are those of the writer and not Gamasutra or its parent company.


There’s an amazing little trick, or rather, feature in GameMaker Studio 2, that you maybe didn’t know about! Let’s take a look at it in this blog.

I was working on the town area for one of my side-projects and was struggling with how to build it. I didn’t want to create a generic, 2D tiled area, with just one layer to fill the background; I wanted it to be a little more complex and three-dimensional, while still being a 2D game...

And you know what GameMaker Studio 2 has? 3D cameras, and depth-based layers.




Let’s learn how this magic can be incorporated into any game!


  • You can download the project here, and have it open on the side, as you read this blog.
  • There is also a video version of this tutorial (but make sure to also read this post as it covers more than the video):




If you’ve used the Room Editor, you’ll know that each layer has a “depth” value. So, a layer with a lower depth would be drawn above a layer that has a higher depth.


Three visible layers with depths 0, 100 and 200


What’s interesting about this, is that the depth value actually corresponds to the “z” value in 3D space! So the third dimension, z, would be the distance of a layer from the camera.

This means that we only need to set up our 3D camera properly, and the rest will be handled by the layer system!


Using a 3D camera in GML mainly consists of setting up a projection matrix and a view matrix.

  • The view matrix defines where the camera is, and where it’s looking.

  • The projection matrix defines how the world is rendered; for example, whether the view is orthographic or perspective, what the field of view is, etc.


The view matrix can be set up using the function “matrix_build_lookat”:

var _viewMat = matrix_build_lookat(cam_x, cam_y, cam_z, look_x, look_y, look_z, 0, 1, 0);

The function name itself explains how it works: it lets you “look at” a point in 3D space, from another point.

The arguments “cam_x, cam_y, cam_z” represent the 3D position of the camera. Then, the arguments “look_x, look_y, look_z” represent the 3D point where the camera is looking.

And finally, the last three arguments “0, 1, 0” (in order: x, y, z) represent the “UP” vector of the camera. It’s basically the camera asking you: “which direction is up?”

Although we are setting up a 3D world, we still want it to retain the old 2D view, where x represents left-to-right movement and y represents up-to-down movement. So to set the UP vector on the y axis, we set the UP vector to “0, 1, 0” (again: x, y, z).


Our projection matrix will be set up using the function “matrix_build_projection_perspective_fov”:

var _projMat = matrix_build_projection_perspective_fov(70, 16/9, 3, 30000);

For the projection matrix, these are the arguments, in order: FOV, Aspect Ratio, ZNear, ZFar

So we pass in the field of view of the camera, and the aspect ratio that it needs to maintain. And then we pass in the ZNear and ZFar values. Anything drawn outside of this z range, whether it’s too close to the camera, or too far away, will not be rendered.


Before starting the implementation, make sure you have these things ready inside your room:

  1. Layers, with proper depth order. Additionally, you can use the padlock button next to the layer depth, to set a custom depth value.
  2. camera view following an object, which can easily be set up in the Room Properties.
    a. If your camera view is set up through code, make sure that it is set up before you run the code below.

This is what I’ll be working with:


A simple 2D game -- without anything “3D”


For managing the 3D camera, we’re gonna use a separate “oCamera” object. Alternatively, you can also do this in a game controller/manager object, if you already have one.

Now let’s set up some variables, in the Create event:

// Camera
camera  = view_camera[0];

// 3D camera properties
camDist = -300;
camFov  = 90;
camAsp  = camera_get_view_width(camera) / camera_get_view_height(camera);

First, we get the camera ID, and store it in camera.

Then we set up some 3D camera properties:

  • camDist: z value where the camera is positioned
  • camFov: Field of view
  • camAsp: Aspect ratio

Now the magic will happen in the Draw Begin event.

Why Draw Begin, you ask? Because it runs before the Draw events, and the 3D camera needs to be updated before anything else is drawn.

// Update 3D camera
var _camW   = camera_get_view_width(camera);
var _camH   = camera_get_view_height(camera);
var _camX   = camera_get_view_x(camera) + _camW / 2;
var _camY   = camera_get_view_y(camera) + _camH / 2;

var _viewMat = matrix_build_lookat(_camX, _camY, camDist, _camX, _camY, 0, 0, 1, 0);
var _projMat = matrix_build_projection_perspective_fov(camFov, camAsp, 3, 30000);

camera_set_view_mat(camera, _viewMat);
camera_set_proj_mat(camera, _projMat);


First, we get the width and the height of our camera, in _camW and _camH. Then we set up the position of the 3D camera, in _camX and _camY. You can see that it points to the center of the camera (by adding half the size to the camera position).

After that we set up the view matrix. The 3D camera is at (_camX, _camY, camDist), and is looking at (_camX, _camY, 0). So between these vectors, only the z value is different.

For the projection matrix, we simply pass in the FOV and aspect ratio using the variables, and then the ZNear and ZFar values.

Then we apply both matrices to the camera, and at the end, use camera_apply. This applies all the changes to the camera immediately, instead of waiting for the next step to update it.




Boom! Our layers now have real depth: you can tell by the neat parallax effect.

But we’re not done yet! Now we only need to have…



Let’s take a look at our view matrix:

matrix_build_lookat(_camX, _camY, camDist, _camX, _camY, 0, 0, 1, 0)

The first three arguments represent the 3D position of the camera. So if we offset those coordinates, we can effectively tilt our camera!

So, for example, let’s do this:

matrix_build_lookat(_camX + 100, _camY + 50, camDist, _camX, _camY, 0, 0, 1, 0)

With this offset, and a lower FOV, we get this awesome view:





Let’s say you want to focus on a character, during a cutscene, or to aid in gameplay. You can do that by simply decreasing the camera distance!




Here is a bonus project, that includes code for real-time camera rotation, and simple focusing:


3D cameras are fun! We can use this power (creatively) to spice up our games and make them stand out from the crowd.

If you need any further help with GML or want to discuss this technique, feel free to hop into our Discord community. You can also tweet at me for any questions: @itsmatharoo. DMs are open too.

Happy GameMaking!

Related Jobs

Sucker Punch Productions
Sucker Punch Productions — Bellevue, Washington, United States

Narrative Writer
Sucker Punch Productions
Sucker Punch Productions — Bellevue, Washington, United States

Outsource Artist
Sucker Punch Productions
Sucker Punch Productions — Bellevue, Washington, United States

Sucker Punch Productions
Sucker Punch Productions — Bellevue, Washington, United States

Senior Camera Programmer

Loading Comments

loader image