Custom Camera in Godot, part 1: Transform the Canvas


Welcome to this first tutorial, the first one in our 30 days streak. Over the next few videos, we’re going to look at how to make those room transitions, inspired by Zelda. This is a basic prototype where the camera just jumps from one screen to the next, but we’ll also look at how to add smooth transitions, or how to pause the game and have the camera smoothly go from one screen to the next. In order to do that we have to code a custom camera. In Godot there’s a built-in node called Camera2D,
that you can use when you need a basic camera. The Camera2D just moves everything on the canvas. That’s how you get motion or the illusion of a camera in a game. It’s actually not really the camera that’s moving, it’s everything on the screen. This camera has some nice properties.
It follows its parent, but on top of that by default, you get a nice little save box. If I don’t move the character too much, the camera doesn’t move. But if I hit the limit of a rectangle that’s in the center of the screen, the character will. If you want to make something a bit more complex,
you’ll have to code it yourself. For that, Godot provides a few functions, that allow you to control the canvas transform. That’s what we’re looking at in this video. The project is available on Github, and here’s what we’re going to make in this tutorial: A simple camera that follows the player. It’s just centered on it, it’s very basic, but that’s the starting point to make a very elegant and powerful custom camera. The starting project looks like that: you have a background, with which the character collides,
and a simple top down character controller. I’ve designed the scene that way: you have a View at the top, which is going to hold the camera script. Below that you have the level, let’s say Tileset, and the player. Let’s take a look at the character script. In order to make a camera, we can do it in two ways. We could set the view on the character’s position, right from within that script, and this would work. The only problem is we are putting two very different systems inside the same script if we do that. We want the camera to be completely separate from the player. With Godot, it’s fairly easy to do. We’re going to use signals. If you don’t know signals, I invite you to read the docs, I’ll put a link in the description, and I’ll do a tutorial on these later. If you’ve never heard about signals, they’re basically the applied version of the observer pattern. You can find the signals in the node tab, on the right side of the screen, and these are some kind of advanced system. When something happens on the current node, it will send a message, and other nodes can listen to it. To make a camera, we want to know when the player moves, and when it moves, we’re going to update the camera. We can create a signal for that. Open the player script, we can use the keyword “signal” to create a new signal. At the top of the script, just write “signal” and I’ll call it “move”. Whenever the player moves, we want to emit that signal. Down the script, after the player has moved,
I’m going to add a new condition if he is moving. That is_moving is a boolean value, that just checks if the player has used one of the arrow keys. We’re going to emit the signal. You have to call the emit_signal function, and you have to write its name. The name is “move”. Now the thing is there is nothing to listen to that signal. So now we’ve got to connect some other object to it. We can use the editor to do that. So select the player, and now you’ll see a new signal,
called “move” at the very top, and you can see it’s defined in player.gd, our current script. We want to connect it to the view, to the camera. But before we can do that, we must add a script to it. Select the View node, add a script, and I’ll just call it camera.gd. Let’s delete everything but the
“extends Node”. Now we need a method, a function that will get called whenever the player moves. Let’s call it update_camera. And it won’t take any parameter. It’s going to be a simple procedural function. Once we have that, I’ll just write “pass”. Maybe let’s add a “print” statement, just so that we see it works. Now we have to connect the signal to the view. So that whenever the player moves, the update_camera function gets called. Let’s select the player, go to the node tab,
and double-click on the move signal. Then you have to select which node to send that signal to, or which node will listen to it (that will be the view), and at the bottom of the screen, you can define the method that will get called when that signal is emitted. And it will be update_camera. Uncheck the “make function” on the right side of the screen, because we’ve already written our function. Now it’s connected. When the move signal is emitted (that’s the red icon), the update_camera function receives it, gets calls. Let’s now play the game to see if it works or not. So, nothing gets printed, meaning that the function doesn’t get called, and we’re not moving the character, so it makes sense. Now if I start to move the character, you will see the print statements appear at the bottom. We’re not going to move the camera per say, but instead we’re going to transform the canvas: the layer on which everything gets drawn on the screen. And that is how you create a camera. I invite you to read that article in the background, that explains a bit how the coordinate system works in Godot’s 2D games. Basically, our canvas transform is a matrix that contains three Vector2s. Let’s get it now: we’re going to add the _ready function, because we want to store some values only when all the nodes in the game have been created. So, let’s add a variable called canvas_transform in order to get the current canvas transform, this current matrix that contains the coordinate system ,alignment, and the origin of the canvas. We want to get the current viewport, and then get canvas transform “.get_canvas_transform” A Matrix32 is like an Array, with three Vector2s inside of it. And if we take the third one (so we call canvas_ transform[2]), this allows us to set the offset of the canvas. In the _ready function we’re going to set the camera to be centered on the player. To do that, we have to get the player node, and then, we’ll call the get_pos. Now we are not modifying the camera’s position just yet. We have to send it our new matrix. We’ve just modified the canvas_transform variable. If we want to update the camera’s position, we have to call get_viewport.set_canvas_transform,
and we pass it our new matrix. You can try out the game, and you will see that it is completely messed up. Yes, it’s moving, but it’s actually not moving in the right direction. The transforms are inversed when we modify that canvas’ origin. So we have to put a minus sign in front of our calculation, in order to move the camera in the right direction. And now the player is there, but it’s in the top-left corner of the screen. We’re moving the top left corner of the camera,
we’re not moving the center of it really. In order to Center it on the character, we have to shift it by a half the screen’s width and height. These values are defined in your project settings. In the display category, you can see width and height. So we can get them from the script. To get one of these global values, we have to use the Global’s class, and call the get() function. Let’s create a new variable. When the game is ready, we’ll create the
screen_size variable, and set it to a Vector2. We have two values that we need: the width and the height. The first one will be Globals.get(display/width). which is the value that we want. Copy your statements, and you just want to replace “width” by “height”. Our screen_size variable holds the width and the height of the screen. And as I told you, the transform is inverted, so we don’t have to subtract it to move the camera in the right direction, but to add it this time around. So let’s add the screen_size, divided by two. And now if we try the game, the camera should be centered on the character. There we go! But if we move the character, the camera doesn’t update. And when we modify the project’s resolution, or if we reuse the script for another game, it will work. With the exact same method, we can update the camera’s position, based on the players movement, but we’ll do that in the next tutorial, because the Kickstarter’support has been completely overwhelming. That was not expected at all, and I’ve got a lot more work than planned as you might imagine. I thank you kindly for your understanding, and moving forward, I have a bit more breathing room, so it’s going to get better hopefully. Thank You kindly for watching, see you tomorrow for the next part !

23 Comments

Add a Comment

Your email address will not be published. Required fields are marked *