GameMaker Studio 2: Action RPG Tutorial (Part 1: Moving & Resolution)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Hello everyone I'm Shaun Spalding and this is the first part of a long tutorial series that will show you how to build your own Zelda-like Action-rpg from scratch in GameMaker Studio 2. We're going to be covering top-down movement and animation talking to NPCs, branching dialogue and fulfilling objectives buying items from a store, fighting monsters, using various unique items and a whole bunch more. There's a demo available in the description so you can go ahead and play the thing that we're actually going to make. And you can also use that page to get access to each episode's source code as it releases. This is a beginner to intermediate level tutorial series and while I will be delivering my usual style of thorough explanations for each piece of code, in order to actually get through this series I won't have time to break down everything so if you're a total beginner to coding anything at all I might recommend my Complete Platformer tutorial series first maybe not the whole thing, but at least until you get the hang of things. But as long as you've either coded before in other languages or you've even a *little* bit of experience coding GameMaker you should be just fine. We're gonna be opening up just by setting up our game resolution, bringing in our player sprite and moving it around the room. Humble beginnings. So let's open GameMaker Studio 2, start a new GML project and let's get started! So the very first thing I'm gonna do and our totally blank project is bring in the sprite for our hero, our player character: Lulu the cat. So I'm gonna right click anywhere in the workspace, go to resources and hit Create Sprite or we can also press ALT+S as you can see. Then I'm gonna click import. Now in this folder you can see I actually have all of the assets that I've handmade for this entire series, all in one folder you can download this folder from the video description if you just want to follow along and don't want to spend time making all your own sprites and that kind of thing. I will be explaining kind of how each sprite is set up, as I bring them in so if you do want to make your own artwork (and I highly recommend doing so) you'll be able to follow along there as well but to speed things up for us throughout the series I've gone ahead and made all the artwork ahead of time. So if you have these assets available to you, The sprite I'm looking for is called "sPlayer_strip4.png" now if I - before actually bringing this in - if I just right-click and hit open and get it kind of in a thing here you can see this image as a transparent PNG with our four frames all spread evenly apart. And because I've done that and because I've named it with this "_strip4" at the end, GameMaker knows when I bring it in that it's an image made of four frames. ...And you can see it's automatically split it up into four frames, okay. What I'm doing here with the player sprite, and what I'm doing with a lot of our player sprites going forward is I'm actually containing all four directions of movement, that we want our player to be able to face-we'll actually be able to move in eight but animation wise we're gonna stick to four, because doing eight directions of movement for every type of animation will take a lot of time and isn't really necessary for what we want to do-so what I'm doing is out for all four directions I'm keeping all the animation for each direction in one sprite. Then we're going to use some code to dynamically play the animation rather than just relying on the sprite framework to play an animation for us. When you're making a top-down game it's very handy to do something like this because otherwise you're gonna need a separate sprite for up, another sprite for left, right, down and so on. So if you can find a way - as we're going to - to contain all the directions for a given sprite in one sprite, do so. The code for that won't be until quite a bit later but just so you understand why on earth we've got like four directions in one sprite. If you play the animation he just spins around. But obviously that's not the animation we're actually gonna have in the final game. So now we've brought the sprite in I'm gonna give it a name I'm gonna call it "sPlayer". And so again if you want to set up your own version of this sprite you want to make four directions for your player character standing idle: starting with facing to the right, then facing up, then facing left and then facing down in that order okay it's very important that they are in that order if you make it and it'll be that same order throughout our player sprites for running, jumping and all that kind of stuff later on. It'll be first facing right, then up, then left, then down. It's to do with how directions work in GameMaker. It'll become clearer later on, but that's the order in which you want to do it, okay. The origin for the sprite - this is always important - it's gonna be in the bottom center. That's gonna be where our player is considered to *actually be* in the game world at any point in time. Because even though it's top-down it's also sort of not, because we've got kind of a front-facing view here and a sorta side facing thing here okay we can still actually *see* the character. It's more of a 3/4 view right? rather than strict "top-down" top-down. So in order to represent that: the origin (or where we consider the player to actually be when they're in the game) is going to be in the *bottom center* of the player sprite. And the other thing we want to set up is the collision mask. We don't want a full, like rectangle for the whole thing so I'm gonna go to manual. I want to define this manually and I'm gonna set it to be around this kind of size, okay? Maybe bring it up one... that looks about right to me, I think, yeah. So here I've got a 4 left, right 12, 14 top and bottom 23. I just want kind of a vague square here to represent the area of space we want to be collidable as our player is walking around. Like our head probably isn't gonna count. We could probably even make it smaller because it's kind of like *this* sort of area of the player that we're gonna want to consider collidable, okay? Adjust as you need to. This feels about right for my particular player sprite, we might come back to adjust it later depending on various other things but for now this will do fine. So now that we have an image to represent our hero and our player, we can go ahead and close this and create an object; which is going to contain all the logic and interactivity of our player. We're gonna name it "oPlayer" to start with and then we're going to assign it sprite to match sPlayer. The next thing we're gonna do and get this out of the way nice and early is set collision mask to not be "same as sprite" but to specifically label it as sPlayer. Ok? that's important you might think "what's the difference?" Same as sprite would mean that it is sPlayer right? but what we're gonna do throughout the game we're obviously gonna animate our player, they're gonna change sprite from sPlayer to sPlayer_walk or sPlayer_roll and various other different things and what we don't want is for our collision mask to then change *with* the change in sprite and to have to set up that collision mask to be the same on every single sprite. What we do is just set the collision mask to strictly be sPlayer, so that when our sprite changes our collision mask does not, and it stays as that collision mask that we set up in our sprite. Now before we get into the events and logic that control the player let's take a look at our first room. So you should have a room by default called room0 here. You open that up you'll just get this big blank rectangle, and we're going to go ahead and change the width and height of the room. I'm gonna change mine to 320 by 180 okay? This is gonna be the resolution of of the game: 320 x 180. we're going to use this room to kind of define that. Now you can use whatever resolution you like, you can use a much higher resolution. The reason I'm going for this one is 1. because it's the the resolution all my sprites were designed for - this is a tiny 16 x 24 sprite - and also 320 x 180 is a useful resolution as it divides very cleanly into a lot of popular 16:9 resolutions. And since it divides evenly into them you're not going to get any problems when you scale up your pixel art to those most common resolutions it's very simple, it'll happen automatically, allow us to go fullscreen very easily and run in a window of various different sizes. So we've set it to 320 x 180 here, but um and if I just click on the instances layer click on a player and just place them somewhere in the game room. And then if I run the game, you can see them spinning around as well, because the player sprite is obviously animating at the moment. But you probably can't see much at all here and that's because I actually use a 4k monitor so this is going to be no good for you following the series. on a 1080p monitor it might like be a bit bigger and be about this much, and you might be able to see it but what you might want to do - you definitely want to do this if you're using a 4k monitor and if you don't you probably still want to do it just so you can see a bit more - is we're gonna stretch the viewport up. So if I go to Viewports and Cameras and click Enable Viewports and then go to Viewport 0 and click Visible. You can see we get this a white rectangle that showed up that's way bigger than our game room at the moment and this is the camera that we're gonna set up. So we're gonna change the the width and height of the camera - of what it can actually see in the game world - to be what our room size is, so 320 x 180 which changes that white rectangle to match. But then in Viewport, which is how big it is in the game window, we're gonna change width and height to be... for me I'm gonna change it to 1280 x 720. If you're following along on 1080p resolution you might want to use 640 x 360. And I'll show you that now. If I run that, you see it's still a bit small for me but for most people on a 1080p monitor that'll be nice and big, but I'm gonna do 1280 x 720... ...and that's a bit nicer so we can kind of see what's actually going on. If you are on a 1080p monitor and you use 640 x 360 the size proportionally should kind of match up to what I've got now. It is important to get all this stuff sorted out as early as you can in your project, that's why we're doing it now before we even have any game logic. We're deciding on our resolution and what it's going to be, so we can keep things consistent. So I'm gonna close that now, and the other thing we want to do with our resolution very quickly it's go to Game Options, come to Windows, and then Graphics. If you tick Allow Fullscreen Switching, what this will do is that will enable a shortcut in your game just built into the runner, so if you press Alt and Enter at any point, the game will automatically swap to full screen. It's arguable that you might want to do a manual setup of this, but this is more than good enough for our game and what we're gonna make. And I'm also gonna make sure here in the scaling, it is on by default but just make sure "Keeps aspect ratio" is ticked. Because we're using this resolution, on most 16:9 monitors you're not going to have much problem with scaling because we're using a very low resolution that scales nicely to 720 and 1080p and 4k. But if you do put the game on like a 4:3 monitor, it will end up producing black bars when you scale up, but it'll stop your art from stretching and looking weird. If you want to know more about working with different resolutions that aren't 16:9 and how to deal with black bars and resolutions and things like that, there's a very good - I think it's a two part series - by Pixelated Pope on resolution and aspect ratio management. He goes over everything in a lot of detail, I highly recommend going there checking that out if that's something that interests you for your specific project or your game. But as I said, for what we're doing just setting this up, ticking this on and using this specific resolution and "keep aspect ratio", "allow fullscreen switching" is more than good enough for what we want. So if I run the game now, you see our black window here and if I press Alt+Enter, you can see it's given this full screen and the pixels are nice and clean they're not stretched or distorted in any way. Because it scaled right up all the way to 4k with no problem and it would to 1080p as well. So now that was a lot of setup but I promise you it was very important, but now we can finally go ahead and start writing some actual game code. In fact if I just close the workspace down just to get rid of everything and click this plus make a brand-new workspace that's nice and clean - just a quick way of doing that - and then we'll close the room as well and get rid of that on the side. Open our player object, nice and blank and click "Add event" and add the Create event. You'll get an empty code window. Now most of the time, if I'm working on a code window, what I want to do first of all is maximize it. I personally make the font nice and big but that's just more for the benefit for you guys watching the video, depending on what device you're watching it on it might be harder for you to see etc. But you can shrink and increase the font size with f7 and f8 in case you are interested in how I'm doing that. There might be some default text here that doesn't match what I've got here but get rid of anything that's in the code window when you first create it so it is just totally blank. The first thing I'm gonna type in here is image_speed. And you can see that's come up in the autocomplete as well, when I finish it turns green and set that to zero. That's gonna stop us spinning around, because as I said we're going to manually handle all of our animations later on. So we're going to turn image speed to zero so that basically disables the automatic animation of this particular object. Then I'm gonna declare a couple more variables. hSpeed equals zero, that's going to represent our horizontal speed. vSpeed equals zero that's gonna be our vertical speed at any given time. The only other variable I'm going to define is how fast we are able to move when we want to walk. I'm gonna set that to two pixels per frames when I set speedWalk to equal 2.0. It should be stated I have my own naming conventions for variables and you can use whatever you like but just try and be consistent, okay? You don't have to use all the variables I use - it'll make it easier, certainly, to follow along with the tutorial - but I have my personal naming conventions, you can use whatever you like. Now before we go any further I'll just run that just to demonstrate that image_speed = 0, you can see there, it stopped us from animating. So now we're going to add the Step event for the player which controls everything that happens every single "game step", the vast majority of our player logic is gonna go in there. Now we could go back to the workspace and click add event again but what I'm gonna do is just right click in the code editor, of oPlayer: create - just to keep us in full screen - and go to add/open event and add the step -> step event. Then that adds the step event. If I go back to that workspace you can see it's in there now, and we're ready to start adding code to that. Just a quicker way to do that for you. Now the first thing we want to do with the player on any given frame is find out what keys the player is actually pressing. So then we can work out what to do with those inputs later: whether we're moving, activating something, attacking, opening the menu, and so on. So we're gonna have a whole bunch of variables that store the state of various different keys on our keyboard. so keyLeft that's gonna be about whether or not we're pressing the left arrow key (or the A key as we'll come to in a bit) but for now just whether or not we're pressing the left arrow keys. So I'm type keyboard_check(vk_left), which is obviously going to return True if we're currently pressing the left arrow key and False if we're not. So that's one if we're going left or zero if we're we're not pressing the left key. And then very similarly keyRight equals keyboard_check(vk_right); Same thing for the right arrow key. Now I'll just quickly do keyUp and keyDown... ...That's exactly the same thing. Remember to end every line of code with a semicolon. GameMaker actually lets you get away with not using semicolons in a lot of places, but please do, it's very good practice to declare the end of a function or a line of code. Oh and let's fix this here, as you can see this is blue because I spelled this wrong, there's a handy little spellcheck for you, there let's change that to that. Now I'm gonna establish a couple more button presses just for things that won't be used in this episode but will be used later just makes sense to get them all in now so we don't have to keep coming back to this part of the step event. Okay so I'm going to do keyActivate = keyboard_check_pressed(); Slightly different function. So keyboard_check(); tells us whether or not the key is currently down or not, pressed tells us whether or not it was pressed in *this particular step*. So if key_activate - that's going to be when we talk to NPCs, activate objects, pick things up and so on - that's gonna be the spacebar now I'm gonna do just a couple more of these real quick... so I've added keyAttack in here, which is for attacking things, that's going to be our shift key. We're gonna use the ctrl key on the keyboard for activating "items", things like the hookshot and the bow, that are going to come a lot later in the series. Just getting them in there now. The other thing is - I'm just gonna shrink the font a little because I'll need some space on the end of these lines - now these are all like well known keys on the keyboard that that you can do with vk_left, right and so on. If you want to use individual letters it works a little bit differently. We have to use what's called the Ord() function. So say we don't want to use the left arrow key and we want to use "A" like for doing WSAD right? instead of vk_left I would type the ord() and in those closed brackets we put two quotation marks. And then in between those two quotation marks we put the letter that we want to use. Don't worry too much about it, that's just how you do it. That's just how you get the letter in relation to the keyboard. As compared to vk_right, vk_up It's interchangeable, if you want a letter on the keyboard that's the function you use. Middle click it to bring up the manual if you want to learn more about it, but that's all you need to know about that for now. The other thing is what if we want to check two keys. Well we can actually do that we can check, technically, as many keys as we want in one particular variable. What we can do is, because this will just return True or False, what we can do is combine it with another thing that will contain True or False and we perform a logical "Or" statement with it. so if I type "or" there you can see it has bolded and gone yellow. And I can put a keyboard_check(ord("A")) in and if I just change this one back to vk_left. So what an "Or" does is it returns True if *either* one of the two functions are True. And they both have to be False in order for it to return False. So if either one of them is True this will become 1 or True. The other way we can type "Or" as well, there's two ways to do it you can either write it like that or you can write it with two vertical pipes ("||") I believe they're called. This is where it is on my keyboard it might be somewhere else on your keyboard I don't know, but there it is for me. Once you do that remember to put a semicolon at the end of here, to declare exactly where the end of this particular line of code is. I'm gonna do the same thing for right, up and down We're gonna "Or" them with their WSAD equivalents... So obviously feel free to pause the video, while you go and actually copy those things down. Okay so now that we've got our inputs we know which keys the player is pressing, which keys player is not pressing, we can begin to use that to process what to actually do with our player. Where we want to move, how we want to behave. Now we're gonna do this slightly differently from how we do movement in say, a platform game, where we quite simply move along one axis, left and right every now and again jump we just sort of handle the the two axes fairly independently. Instead we're gonna combine our up and down axis with our left and right axis to find a vector of movement. So we're going to work out - by combining our up and down key, and our left and right key - work out what angle we're trying to move in? and how far to move in that direction. The reason to do it this way is because if we handle top-down movement, if you think about it like moving on a grid, if you've ever played like a grid-based board game you might be familiar with the fact that moving diagonally across a grid moves you way faster in a particular direction than than moving just horizontally, or just vertically. If we were to move two up and two to the right simultaneously that combines to a total distance further than what we want our actual maximum speed to be. Meaning that you're able to move faster just by moving diagonally. We don't want that, so we're gonna do some simple vector stuff to make sure that we're always moving the exact same distance, regardless of whether or not we're moving diagonally. The first thing I'm going to do is use a little clever trick to work out the direction of movement based on all four of these keys. So we're going to combine all four of these keys together to work out a final direction so if we're pressing up and right we should end up with an angle of 45 degrees. That's assuming zero degrees are starting from facing to the right, so like 45 degrees would be facing up right 90 degrees would be facing up and so on. We're gonna get back an *angle*. So inputDirection is what's going to store our angle and it's going to equal point_direction(). That's a function that you can use by plugging two coordinates in and it'll tell you the angle between those two coordinates in your room. The room part of it actually doesn't matter for us we're just going to use the fact that this is a quick way to calculate "a direction" between two different "grid positions" essentially, so my first position is actually going to be 0,0 just kind of as like a "root position" imagine it as the root position on a graph. Then I'm gonna type keyRight - keyLeft So if keyRight = one it means were pressing the right arrow key if keyLeft is one we're pressing the left arrow key, so if we're pressing them both they cancel each other out (because 1-1=0) if we're pressing neither they cancel each other out (0-0=0) but say we're just pressing keyRight: 1 - 0 = 1. So think of that again on on a graph between 0,0 and 1,0 so that gives us one pixel to the right. If we're just pressing the left arrow key it will be 0-1 so we end up with -1. so x will finish on minus 1. Hopefully you can start to see where this is going, if I then put a comma and the second y-coordinate I want to put in is keyDown minus keyUp, close bracket, semicolon. Similar thing, so either 1-0=1 going down. or 0-1= -1 therefore going up. And so say we had -1,-1 we end up with an angle of like 130 degrees, 135 sorry I can't do maths today. Just a reminder in case or just to let you know / in case you're not aware. Angles in GameMaker are always considered as 0 degrees facing to the *right* and then they circle around all the way back to 360 degrees also facing to the right okay so 90 degrees is up, 180 degrees is left, 270 is down. That is also why - if we come into our sprite - we can see we've got a sprite organized in that same way. So our first sprite is 0 degrees, then 90, then 180, then 270. That's why we always start all directions from facing to the right and we go around anti-clockwise. So we have an angle but it might be irrelevant, if say some of these cancel each other out. Like if we're pressing all four arrow keys or whatever right? That might give us an angle of 0, because it's just it'd be 0,0 to 0,0 there is no angle between that. I'm not actually sure what that returns actually. An angle from one point to the same point? probably 0 degrees. So that wouldn't be relevant so we need to make sure that the combination of keys with press is actually giving us a valid magnitude in a given direction. So I'm going to type inputMagnitude then this is simply going to equal True if we want to move in this direction okay or False if we don't actually have a direction to move in. Say we're just pressing the left and right keys simultaneously and nothing else they should cancel each other out. So to do that I'm gonna basically make this an "or" between two conditions the first condition is going to be keyRight - keyLeft != 0 So if keyRight - keyLeft is not zero that means either 1-0 therefore 1 or 0-1 therefore -1. So we either want to be moving to the left or to the right, but if we combine them both together and it equals zero they should they they cancel each other out. So assuming that's not zero it'll come back True so we *do* want to move in whatever this direction is. And the same for up-and-down so I'm gonna do another logical "or" here open bracket keyDown minus keyUp does not equal zero, close bracket, semicolon. And then finally, finally, finally, finally we can actually go about moving the player-character. So I'm gonna write a comment here with two slashes, turns green, "movement" just to label this section. In fact we can go to the top here as well and put a comment in, very good practice as always to comment stuff: "get player input". Okay? Get all player inputs, we calculate some stuff based on it and then we can process that into our players actual movement. so hSpeed - the variable we declared in the create event - is gonna equal... not zero, well it does equal zero at the moment but it's gonna equal lengthdir_x. Now this is a function that we can plug a length and direction into - which we happen to have, we have a magnitude and direction right here - and it'll give us back the X component of that vector. So it'll tell us how far along the x axis we have to travel to move if we were going to make that particular movement happen. So the lengths we're going to provide it is inputMagnitude, which is a 1 or a 0 depending on whether or not we're moving, multiplied by speedWalk. That's our walking speed which we know is 2 pixels per frame. (And then that's the thing you could change if you are holding a button to run or something like that you could do speedRun and so on.) The direction we're gonna give it is obviously inputDirection. That angle that we worked out. And I'm gonna do the same again with vertical speed but instead of lengthdir_x we're gonna use lengthdir_y. So we're gonna get that y component of our movement. We use inputMagnitude multiplied by speed walk, inputDirection again. Exact same inputs because it's the same length, same direction. Then very simply at the bottom x+=hSpeed; y+=vSpeed; So that's everything, you'll notice obviously we have these little warnings still up here that tell us we've only used these variables once, very handy as you build up the code later on to be able to see back if you've declared any variables you haven't actually used throughout your project. That's because we're only gonna use those later on, so again don't worry about those. Let's run the game now if we press f5... We see we've got our game window here he's no longer spinning around as he was before and if I press up, down, left, right you can see we can move around. And our diagonals are not moving us faster than if we were just holding one particular direction ok. And I can do it with the WSAD keys as well. I know you can't see whether or not I'm using the WSAD keys, but trust me you can use both sets of keys and you'll discover that for yourself, as you're coding alongside me. That's everything we're going to cover for this episode as I said we're starting quite simple. The next steps are going to be animating the character and then of course handling walls and collisions. This series is almost wholly supported by my patrons over at patreon.com/ShaunJS Those are the names you see passing by now all lovely people that enable me to do this work and fun it's very existence. If you like the work but I do or you think it's important and helpful for others please consider supporting me and keeping work like this free and accessible for everyone, as well as getting your name in the credits, regular updates and behind the scenes source code and goodies. A huge extra special shout out to the following patrons: [CREDITS]
Info
Channel: Shaun Spalding
Views: 287,975
Rating: undefined out of 5
Keywords: Game Maker (Video Game Engine), Tutorial, GameMaker Tutorial, GameMaker, Game Development, Indie Games, Tutorial Series, Game Maker Studio, Making Games, How to make games, GameMaker Studio 2, GMS, GMS2, ARPG, Zelda, Action RPG, Top Down
Id: upoXH9hAKUg
Channel Id: undefined
Length: 32min 3sec (1923 seconds)
Published: Fri Dec 20 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.