• Hey! Guest! The 39th GMC Jam will take place between November 26th, 12:00 UTC and November 30th, 12:00 UTC. Why not join in! Click here to find out more!

Legacy GM Turn-Based Strategy 101 video series

GM Version: Game Maker: Studio
Target Platform: PC centric
Download: N/A (considering providing individual links for each video if people are interested)
Links: Complete Tutorial Playlist

Summary:
Turn-Based Strategy is a complex genre, this is the first couple bits of my seventeen video series covering the genre. In these first two videos we create our game grid, let each node know who its neighbors are, and then get a basic cursor up and running.

Tutorial:

The bedrock of the Turn-Based Strategy genre is a big game grid that the action takes place one. This consists of a series of "nodes" which the characters stand on, pathfind with, move between, and target.

In order to get all of this up and running, we need a series of these node objects created in a big grid and we need them to know all of the other nodes that neighbor them!

Start by creating a basic sprite. Just a 32x32 white square with a black border. This is our "Node." I call mine sNode, but anything is fine so long as you're consistent.

Now an object for our nodes. I call mine oNode, but whatever makes you happy.

In here, we need only a simple, single line of code:

I have been informed that directly copy-pasting code may result in an error by placing an invisible character at the end of each line of code. If you try to copy-paste and receive this error, try going to the end of each line and hitting backspace.

oNode Create Event

neighbors = ds_list_create();

This neighbors list will hold on to the instance ids of every adjacent node. This will be the cornerstone of our pathfinding system later on down the road, so it's pretty important. For now, this is the only line of code our Node needs.

Before we go any further, let's create a constant. We're going to be tiling the entire game room with these Nodes so we want one every X pixels, where X is the width and height of our node object.

resources -> define macros
GRID_SIZE = 32

For now, this is all our node object needs. So let's create another object, our game controller which will create our grid of node objects and eventually handle our turn order. I call mine oGame.

First thing we need to do is create an array. In here, we will store the instance IDs of every node in our game grid for ease of reference by other objects.

oGame Create Event

globalvar map;

mapWidth = room_width/GRID_SIZE;
mapHeight = room_height/GRID_SIZE;

//create node!
for(xx = 0; xx < mapWidth; xx += 1){
for(yy = 0; yy < mapHeight; yy += 1){
map[xx, yy] = instance_create(xx * GRID_SIZE, yy * GRID_SIZE, oNode);​
}
}

Alright, map is an array. It will hold on to the instance IDs of every node in our room. We can use that later to reference every node by its grid coordinate as we see it on screen.

mapWidth and mapHeight are how wide and tall our game room is in nodes.

That couple of For loops will tile the entire room in nodes and while it does so, it will store the instance ID of each node in the relevant portion of the map array.

Pretty easy so far. We need more here however.

Earlier we did that neighbors list in the oNode object. Every node needs its neighbors list populated with the instance IDs of every adjacent node! We can accomplish this with another couple for loops.

Now, in the first video I cut off after the first 4 cardinal directions (up, down, left, right) and do the diagonals at the start of the second video. For ease, I'm just going to do all of the neighbors here all at once.

We'll do this in the oGame Create Event right below the other couple For loops.

oGame Create Event

//populate neighbor lists!
for(xx = 0; xx < mapWidth; xx += 1){
for(yy = 0; yy < mapHeight; yy += 1){

node = map[xx, yy];

//add left neighbor
if(xx > 0){
ds_list_add(node.neighbors, map[xx - 1, yy]);
}

//add right neighbor
if(xx < mapWidth - 1){
ds_list_add(node.neighbors, map[xx + 1, yy]);
}

//add top neighbor
if(yy > 0){
ds_list_add(node.neighbors, map[xx, yy - 1]);
}

//add bottom neighbor
if(yy < mapHeight - 1){
ds_list_add(node.neighbors, map[xx, yy + 1]);
}

//top left neighbor
if(xx > 0 && yy > 0){
ds_list_add(node.neighbors, map[xx - 1, yy - 1]);
}

//top right neighbor
if(xx < mapWidth - 1 && yy > 0){
ds_list_add(node.neighbors, map[xx + 1, yy - 1]);
}

//bottom left neighbor
if(xx > 0 && yy < mapHeight - 1){
ds_list_add(node.neighbors, map[xx - 1, yy +1]);
}

//bottom right neighbor
if(xx < mapWidth - 1 && yy < mapHeight - 1){
ds_list_add(node.neighbors, map[xx + 1, yy + 1]);
}

}

}


Great! Now we should have our nodes created, and our neighbors list populated. Our nodes are ready.

But there's really no way to ensure that all of this was done properly! So lets add a little debug feature. We'll add a draw event to our nodes and they'll draw how many neighbors they're tracking on their bellies so we can double-check our work!

oNode Draw Event

draw_self();

draw_text(x + 4, y + 4, string(ds_list_size(neighbors)));


Alright, now run your game and you should be looking at something like this:



Nodes in the center should be tracking 8 neighbors.

Nodes along each border should be tracking 5 neighbors.

Nodes in the corners will be tracking 3 neighbors.

If you've got all that right! Great, our nodes are ready to do cool things like grid-based pathfinding in the future!

But there's a few more steps between here and there. We need a cursor, we need some characters, and some terrain before we get started on pathfinding!

Here's the second video of the series where we implement our basic cursor object:


and finally:

A link to my entire Turn-Based Strategy 101 series

Currently 17 videos and over 8 hours of instruction covering a ground-up framework for the Turn-Based Strategy genre. It should give you all the tools you need to make your own TBS games!

If anyone has any questions or comments, I'm more than happy to help.
 
Last edited:

chance

predictably random
Forum Staff
Moderator
This is a good addition to the forum. It's clear you put some effort into these. From reading your in-post tutorial and watching the video, I'd say this should be accessible to intermediate level GML programmers. Many of the concepts you describe are general in nature -- not just limited to strategy games.

Your website has a considerable amount of content, so I'll be looking at the other tutorials you provide there. Seems like a useful resource for GameMaker users.
 
This is a good addition to the forum. It's clear you put some effort into these. From reading your in-post tutorial and watching the video, I'd say this should be accessible to intermediate level GML programmers. Many of the concepts you describe are general in nature -- not just limited to strategy games.

Your website has a considerable amount of content, so I'll be looking at the other tutorials you provide there. Seems like a useful resource for GameMaker users.
Thanks!

I've actually been at the series for a while now, I just never felt it was presentable enough to make a post here (Terrible sound issues in the first half-dozen videos, I only recently rerecorded them).
 

Filuto

Member
Thank you sergeantIndie for this. It helped me make A* pathfinding about 2-3 months ago. Did you rerecord that or is it just reuploaded ?

 
Last edited:
Thank you sergeantIndie for this. It helped me make A* pathfinding about 2-3 months ago. Did you rerecorded that or is it just reuploaded ?

The firs 9 videos or so had terrible sound quality (very sorry you had to suffer through that) so I rerecorded and reuploaded them.

Everything past that is the original recordings because they sounded alright. Series is still plugging along, just released episode 17 last week.

Looks like a neat game you've got going there! Glad I could help!

Intrude looks cool too!
 
U

umetnik

Guest
First of all I would like to applaud you for taking time to write down the tutorial and in a very nice format as well. I prefer written tutorials since many video tutorials tend to have lots of white noise and take too much time to present the ideas. So I hope you will continue in this direction.

I would just like to comment the following code:

for(xx = 0; xx < mapWidth; xx += 1){
for(yy = 0; yy < mapHeight; yy += 1){
map[xx, yy] = instance_create(xx * GRID_SIZE, yy * GRID_SIZE, oNode);​
}
}

1) An optimized way to filling the map array would be to turn both for statements around to get rid of unnecessary array reallocations happening in the background.
2) Variables xx and yy could be declared local using keyword var.
3) Prefix decrement -- could be used instead of -= 1.

Since these optimizations don't make code less readable,I always try to make them. The code would look like this:

for(var xx = mapWidth - 1; xx >= 0; --xx){

for(var yy = mapHeight - 1; yy >= 0; --yy){
map[xx, yy] = instance_create(xx * GRID_SIZE, yy * GRID_SIZE, oNode);​
}
}

If statements for neighbors could also be grouped together to get rid of a few comparisons but I think the code is more readable the way it is now.
 
First of all I would like to applaud you for taking time to write down the tutorial and in a very nice format as well. I prefer written tutorials since many video tutorials tend to have lots of white noise and take too much time to present the ideas. So I hope you will continue in this direction.

I would just like to comment the following code:



1) An optimized way to filling the map array would be to turn both for statements around to get rid of unnecessary array reallocations happening in the background.
2) Variables xx and yy could be declared local using keyword var.
3) Prefix decrement -- could be used instead of -= 1.

Since these optimizations don't make code less readable,I always try to make them. The code would look like this:

for(var xx = mapWidth - 1; xx >= 0; --xx){

for(var yy = mapHeight - 1; yy >= 0; --yy){
map[xx, yy] = instance_create(xx * GRID_SIZE, yy * GRID_SIZE, oNode);​
}
}

If statements for neighbors could also be grouped together to get rid of a few comparisons but I think the code is more readable the way it is now.

Thanks for the suggestions, but a lot of the tutorial was essentially designed to be as easy to follow as possible since the genre itself already makes for a very complicated subject.

So there's a lot of things I do in the series that could be optimized or done a little better, but it is for the sake of readability and ease of instruction to people who are new to concepts. The optimizations might matter, long run, on a super large scale, but they don't matter within the scope of the tutorial.

I've already had comments and questions about both for loops and arrays for example, so introducing these concepts right off the bat in the "better, more complicated way" wouldn't do me much good as an instructor.

As far as more written tutorials go, I hadn't particularly planned on it. If there is real interest I could maybe post them to a blog or whatever, but I'm not sure if the forums would like it if I spammed them with 17 consecutive tutorials, some over an hour long in video format. And then, I'm not even done, so there's probably another 3 or 4 before the 101 series is wrapped and I move on to an advanced series.

edit: as far as incrementing and dercrementing go, I've been using Game Maker for so long that I swear ++ and -- weren't possible when I started. Or for whatever reason, we just didn't use them, I forget. At any rate: It's just a habit I developed in GML that I can't shake for some reason. I do cover that ++ and -- are acceptable alternatives in one of the videos, I just can't seem to shake the habit of += 1.
 
Last edited:
O

OhGoodShepherd

Guest
I started watching this series (on dialup!) in hopes of gaining some understanding of how turn-based games of the roguelike genre function. I was hoping this tutorial might be of that nature but now I am unsure if this would be similar or not - unsure if OP or anyone reading this has played games like Rogue, Nethack, Dungeon Crawl, so on... but I am wondering if this code/tutorial would be of use to someone hoping to create a similar game mechanic. Not wanting to suffer through downloading these at dialup speed if this would not be helpful. Relatedly, anyone know of a tutorial with roguelike turn based movements? (or please tell me if this tutorial is applicable)
 
O

OhGoodShepherd

Guest
I am a dial-up user so I have to be picky about what I queue up to download/watch. Would this video series be helpful to someone who wants to make a turn based game in the RogueLike style? (Rogue, Nethack, Dungeon Crawl)
 
I am a dial-up user so I have to be picky about what I queue up to download/watch. Would this video series be helpful to someone who wants to make a turn based game in the RogueLike style? (Rogue, Nethack, Dungeon Crawl)
Probably not?

You're talking pure, old school, ascii, move one square/attack at a time? This tutorial is more akin to how D&D, XCOM2012, and Final Fantasy Tactics works.

If you're looking for a roguelike tutorial, I know of a pretty darn good one. Not Game Maker unfortunately, but it is a pretty good introduction to Python. Learned a lot doing it.

Best part is: No download! The whole thing is up on the roguebasin wiki! You can find the tutorial here

The first bit even goes through setting up python so if you've never worked in a language like Python before it'll start you on the right track.

Best of luck!
 
O

OhGoodShepherd

Guest
Thanks for taking the time to reply! (Sorry about the double post, thought the webs ate the first one.)

I appreciate this advice and have been able to find a lot of really great NON GM roguelike tutorials and have attempted to learn Python, LUA, JavaScript and made attempts with a small handful of other game creation languages, tools, scripts and what-not ... never had any luck except with Game Maker.

I've made a few simple games and am enjoying myself and learning quite a bit of GML (go help files!)

I understand that a roguelike (ascii or non) is far from a simple task so I was pretty excited when I saw your grid making and thought that was where this was going (not that this isn't an awesome and valuable resource that I'm currently taking attention away from...)

I'm going to keep poking away at GML while using some of those great non-gml tutorials for inspiration and advice.

I think I will watch some more of this series because I learned a lot just from the first few when I watched them way back when, I now realize, with the bad audio.

Thank you so much for making this tutorial and for your reply!
 
G

greentiger

Guest
Interesting looking tutorial, I wish I could find something like this as an isometric game
 
Interesting looking tutorial, I wish I could find something like this as an isometric game
The fundamentals are all exactly the same. The game I'm building in my own time is isometric and it heavily leans on these same things I cover in the tutorial series.

The only difference is essentially placement math. When the terrain and grid itself is placed, you do it with funny math in Isometric and with funny shaped sprites. That's it.

Everything else -- the pathfinding scripts, the method of doing ranged and melee attacks, the AI -- it's all pretty much 1 to 1 translation over from top down to Isometric.

Oh and the math involved in the cursor deciding what node it is hovering over is different. Obviously.

I essentially decided to teach top down rather than isometric because it is already a very complicated genre and I didn't want to over-complicate things. Once by Turn-Based Strategy 101 tutorial series is wrapped I am planning on doing some advanced videos where I'll cover things like Isometric grids (and more advanced AI, different turn order types, etc).
 
G

greentiger

Guest
I essentially decided to teach top down rather than isometric because it is already a very complicated genre and I didn't want to over-complicate things. Once by Turn-Based Strategy 101 tutorial series is wrapped I am planning on doing some advanced videos where I'll cover things like Isometric grids (and more advanced AI, different turn order types, etc).
Right, my stumbling block in LOVE2D was trying to figure out where the mouse cursor was, drawing the grid wasn't a problem.
Looking forward to it.
 
L

lordpoee

Guest
Well. Having a problem. It says, "Unexpected Symbol" Line 13, Pos 30
I'm pretty sure it's a structuring error but I can't pin it down.
Create event for oGame

Solved:

It wasn't a code problem per say. When I copy and pasted the code in the GM environment, it added an invisible character to the end of some lines. Going to the end of each of these lines and pressing back-space removed the non-visible characters and resolved the problem.
 
Last edited by a moderator:
P

Pelican

Guest
Just wanted to give a massive thank you for uploading this, I've been trying to make a turn-based tactical RPG as well, and I noticed how few resources there are. I've gotten the stats and movement down pat, but I'll definitely be looking at your resources! :D
 
Well. Having a problem. It says, "Unexpected Symbol" Line 13, Pos 30
I'm pretty sure it's a structuring error but I can't pin it down.
Create event for oGame

Solved:

It wasn't a code problem per say. When I copy and pasted the code in the GM environment, it added an invisible character to the end of some lines. Going to the end of each of these lines and pressing back-space removed the non-visible characters and resolved the problem.

Interesting. Thats.... weird.

Thanks, I'll make an edit in the main post informing people that direct copy-paste may result in an error.
 

superschure

Member
The fundamentals are all exactly the same. The game I'm building in my own time is isometric and it heavily leans on these same things I cover in the tutorial series.

The only difference is essentially placement math. When the terrain and grid itself is placed, you do it with funny math in Isometric and with funny shaped sprites. That's it.
Firstly, thank you so much for creating this series; the quality and attention to detail is superb and you've made a lot of these concepts and techniques easy to grab a hold of. Mainly I want to ask if you would mind throwing a bone on how to apply the funny math to your style of code for isometric tiles at 45 degrees. (It's technically 30 degress, right?) I've read the isometric tutorials on Clint Bellanger's page and it makes sense to me but my mind blue screens when I try to implement it with your code. It's like I can't get the implementation to understand that the world has shifted 45 degrees so all the tiles just end up broken apart or sandwiched together. I just deleted and started from scratch because it was all over the place. Thanks for your insight.
 
Firstly, thank you so much for creating this series; the quality and attention to detail is superb and you've made a lot of these concepts and techniques easy to grab a hold of. Mainly I want to ask if you would mind throwing a bone on how to apply the funny math to your style of code for isometric tiles at 45 degrees. (It's technically 30 degress, right?) I've read the isometric tutorials on Clint Bellanger's page and it makes sense to me but my mind blue screens when I try to implement it with your code. It's like I can't get the implementation to understand that the world has shifted 45 degrees so all the tiles just end up broken apart or sandwiched together. I just deleted and started from scratch because it was all over the place. Thanks for your insight.
First things first: I am currently planning an "advanced" series that'll cover isometric stuff. As well as other things, of course, but it'll likely start with isometric stuff since that's the grid. The big hold up is that I'm finishing the current tutorial and am also making my own game on the side as well as running the gaming store. Also I plan on covering animation and such so I need sprites, but I'm a terrible pixel artist.

So, there's really not a heck of a lot of math applied to the isometric stuff. There's nothing actually rotated 30 degrees, you just make the sprites for your nodes little diamonds instead of little squares. Make the origin of these little diamonds the top center rather than upper left corner.

Then anything that stands on a grid you have to put their origin so it'll line up properly with the node sprites top center. That makes things line up nicely.

Then there are a couple more important constants. Rather than just having GRID_SIZE you need GRID_WIDTH, GRID_HEIGHT, and GRID_WIDTH_HALF and GRID_HEIGHT_HALF.

Then, essentially, to go to the right you go over GRID_WIDTH_HALF and down GRID_HEIGHT_HALF.

I really shouldn't get too into it here. I realized that I'd just end up more or less retyping what Clint already wrote only without the fancy pictures. I'm not sure if I can explain it any better than he can, and I'm now kind of wondering how I'm going to explain it for the videos.

AT ANY RATE:

It's really easy to convert a game to isometric once you understand the math involved in node placement. For instance, the pathfinding algorithm is exactly the same, so are any methods that rely on Manhattan Distance to figure out how far apart two squares are on a grid. Any of the stuff involved in your game's actual mechanics (attacking, hit rolls, leveling up, etc) and anything that relies on how the grid works are also unaffected.

So really, it's only placement of the grid that is different. So don't think you have to hold off on learning this stuff to begin planning and designing a game of your own. You can build whatever you want and then once you understand the isometric stuff you can pretty easily go back and change the placement. You can certainly begin prototyping at least.

So go ahead and do whatever you want to do now in "top down," and once I get up off my lazy butt and do the Isometric stuff you can port it over without much fuss. Or keep poking at Clint's article, or others until you have a better grip on it. I certainly can't explain it any better here, in text without pretty pictures, than Clint did in his article.

sidenote: I've only just now realized that Clint looks like Ed McMillan's classier brother.
 

FrostyCat

Member
There is no need to cover isometric separately.

The grid is still north-east-south-west, the isometric projection is just a presentation. The code you are responsible for teaching hasn't changed, it's just the drawing code that's different. GMS comes with an example showing how to draw an isometric view off a top-down map.

Anyone attempting AI work on isometric coordinates is doing it wrong.
 
There is no need to cover isometric separately.

The grid is still north-east-south-west, the isometric projection is just a presentation. The code you are responsible for teaching hasn't changed, it's just the drawing code that's different. GMS comes with an example showing how to draw an isometric view off a top-down map.

Anyone attempting AI work on isometric coordinates is doing it wrong.
Yes, all of the nodes are still stored, as data, in a normal X/Y grid format. They're just actually placed in the room appropriately so they don't need to be drawn with some silly offset.

Which is important since we use the location of these node objects to put points on our path when moving from point to point.

In either case, the math of placing them appropriately (or drawing them with some funky offset) is more complicated than just tiling a whole room with a grid and therefore is going to be covered in my "advanced" series when I get around to it.
 

superschure

Member
...sagely advice...
Yeah man, I did all of that. I made diamond sprites with correct origins, created the GRID_""_HALF style macros, have isometric style sprites ready to drop in but I'm still off with effecting the placement of the nodes, pretty close but just off. It's obviously my grasp of the code necessary that's lacking so I'm going to study Clint's explanations a lot more and continue building in 2D until my understanding improves and I have that eureka moment and/or your own explanations come to fruition in the future. Hell, I'm only on your 3rd video so far, lol. And you're dead on about Clint's appearance.
 
C

CydoEntis

Guest
I plan to watch all the tutorials anyway but if I were to watch all of these would I be able to use the knowledge acquired to make a board game similar to chess?
 
G

greentiger

Guest
There is no need to cover isometric separately.
I disagree, just because you understand the concept or the changes involved doesn't mean that there aren't others that don't need something explained or pointed out.
 
P

Pelican

Guest
sergeantIndie, I think isometry can be covered in a separate tutorial, since isometry is pretty consistent for any kind of game, be it movement in a tactical RPG, a normal RPG, or even an action game. In fact... I have a Paint document of all my isometric calculations (I'm making an isometric turn based tactical RPG independently), so I could put it on here. ;)
 

FrostyCat

Member
I disagree, just because you understand the concept or the changes involved doesn't mean that there aren't others that don't need something explained or pointed out.
While there is a need for a tutorial on isometric projections, there is no need for an isometric version of this particular tutorial. Graphical concerns should have no binding on data-level concerns, anyone who doesn't separate them is doing it wrong. That is what my statement earlier on was meant to point out.
 
M

Mylon

Guest
This is a great tutorial. Is there any plan to provide a written version of the other episodes? What about a download of the post-tutorial project?
 
M

Mylon

Guest
Youtube auto-generates transcripts. These will be handy to help re-find specific events in the tutorial series.

Episode 2:
Code:
0:00a YouTube I'm Sergeant Andy welcome back to my turn based strategy tutorial for
0:04GameMaker studio this video we're going to be coding the foundation of our
0:07cursor object but first as promised we're going to go ahead and do the
0:11diagonal neighbors
0:13so it's good or no game object right into its create event and then right
0:19here at the bottom
0:21we're just going to keep going so top
0:24yop top left the top left neighbor and then if x x is greater than 0 and y y is
0:35greater than 0 dds list add node . neighbors map
0:44XX minus one YY minus one great except we need that we need parentheses before
0:55i go on any further
0:56some people have complained that wherever they're at their missing
1:00certain symbols on the keyboard so if you ever see me type this this is that
1:03and you know that and is a keyword if you see me type one of these
1:08that's an oar oar is a keyword and I don't know what the symbol for X or is
1:13and i never use XO really but X or is also a keyboard if it ever does come up
1:17so your sweet I penny of that don't panic
1:21there are keywords so top right
1:25neighbor if X X less than map with minus 1 and y y is greater than 0 dds list add
1:38node . neighbors map
1:42XX + + 1 YY minus 1
1:47great i should scroll down a bit to try to keep this all that i'm working on
1:53kind of in the middle of screen so bottom left neighbor if x is greater
2:00than 0 and y y is less than map height my pipe
2:07- 1 D s list add node . neighbors map
2:15XX + 1 why why
2:19that's wrong plus one that's wrong already
2:23that's all this is this is a minus 1 what do i do it
2:26so x minus one YY plus one great and then bottom right neighbor if x is less
2:36than map with minus 1 + YY minus map height minus 1
2:44des yes
2:48come on list add know . neighbors map
2:53XX plus one YY plus 1 so quick check everywhere we've got a - well first off
3:01everywhere we got an ex it's with and why its height so let's do that real
3:05quick just a quick visual check
3:07so y + height x and with these are both right
3:11great everywhere we've got a minus 1 we need a plus one in here so minus one
3:16minus one plus one plus one minus one plus one
3:19and obviously otherwise it's a minus 1 so minus one plus one minus one and
3:24great
3:24this all looks fine awesome I'm gonna leave this whole thing up for just a
3:31minute
3:31if you're falling behind if you think you made any mistakes you go ahead and
3:34posit now double check and let's green check all the way out and let's run it
3:41because we can test this one right off the bat
3:43okay great
3:46so looks great to me what you're checking for
3:50is all of these ones in the middle in the meat here they should have ate
3:54neighbors thats 1 2 3 4 5 6 7 8
3:57these guys along the side should have five like this guy 1 2 3 4 5 and the
4:02ones in the corner should have three cuz one two three
4:05so check all your corners to call your sides and if what you're looking at is
4:09what I'm looking at
4:10great if not go ahead and go back and try to find out where you went wrong
4:15let's X at it here and now we need to do some cursor and mouse stuff
4:21so first we'll need a new sprite i'm going to call it s cursor s curse or
4:30edit sprite new sprite 32 by 32 is just fine
4:36or one is a nice bright red color and get our line tool and we just want like
4:43four lines going down here we want this thing to be super easy to see and then
4:50we want to go diagonal corner corner and just fill this out until you think it
4:55looks good really
4:56and this fine still looks a little little little scrawny to me so I want to
5:03do a couple more
5:04alright so that's it for me I think that's good enough
5:09awesome green check out of here and we're not done here yet
5:15what we're going to do is we're going to copy that image we're going to paste it
5:18so we got a total of four images and we're just going to leave them all
5:22exactly the same just like that you'll understand why in just a minute but
5:27green check out here and we're not done here our origin 0 0 that's perfect we
5:32just wanted to be the very top left corner of this sprite great but our mask
5:37is wrong
5:37let's modify the mask manual and we only want the very top left pixel to count
5:44for this cursor object when it goes to do its collision checking or whatever
5:48because we just wanted to be the very tip .
5:51so left 0 right one
5:54top 0 bottom one just a teeny tiny single pixel for clicking on stuff
6:00green check out of there and they're great
6:04we need one more sprite though what we're going to do right now is just
6:09create another sprite
6:12i'm going to call this is selected at it sprite sprite 32 by 32 is perfect
6:21let's get in here and let's grab ourselves a yellow and our line tool and
6:25we're going to go all the way around the edge twice because you want our border
6:30to be nice and bold so we want it to be too deep just like that
6:38ok now green check out of here we're going to copy this and we're going to
6:43paste it three more times total of four images again this time we're going to
6:47our second image image one because it starts at zero and try to make the best
6:52orange you can muster out of this little silly color palette here
6:58I'm going good bright orange let's go with that and we're going to set our
7:02opacity to somewhere around 70
7:04close enough get our fill the bucket and fill once then go to the next sprite or
7:11next image
7:12Phil twice then go to the last image and fill one two three times
7:18green check out of here and what we're left with you can see in this preview is
7:22this nice little square that sort of flashes from yellow to orange what we're
7:27going to do with this is we're going to have drawn on top of which ever knowed
7:31the cursor is currently offering over so that like oh well this is the note that
7:35were currently selecting great
7:38green check out here origins fine don't get a mess with the mask
7:41it's just perfect just the way it is so green check out of there
7:44- okay now it's time to make our cursor object so create object call it Oh
7:50cursor give it the appropriate sprite s cursor
7:55let's set its depth to like negative 3
7:59for now I mean work we're going to change this later that like the depths
8:02of layers of things are going to matter eventually for now we just kind of
8:05wanted vaguely on top of everything and that's fine we'll add an event create
8:12event
8:12dragon a block of code and let's do some code
8:16ok so grid x equals 0 grid
8:21y equals 0 and hover node equals no one
8:31ok so great acts and grid why they're there to keep track of where the cursor
8:36is in relation to that map array we made last episode
8:40hover node is going to hold on to the ID of whichever know the cursor is
8:45currently positioned over and no one is a key word in it essentially acts like
8:50no right up my guy i like to use it when the code expect to find itself an ID
8:54because it makes it because then I think oh well it's expecting to find an ID
8:59here
8:59it's important to be able to read your code easily at like a month after you've
9:03written it
9:04so anyway it also returns negative one and you can use that to your advantage
9:08in some places
9:09thats it for our great event there's going to be a lot more in here
9:13eventually
9:14I think but for now super simple
9:17right now it's time for our first step event which is interesting where two
9:22videos in and this is our first step event but that's just how it works
9:27so the first thing we need to do is update our cursor position
9:31so x equals mouse underscore x
9:36y equals mouse underscore why mouse underscore X and mouse underscore why
9:41they keep track of the position in x and y of where the actual windows mouse
9:46cursor is or mac if you're on a Mac er I suppose linux are we doing we're doing
9:51linux
9:52I don't know anyway so this will just update this object's position to be
9:57wherever the cursor for the your operating system proper is and
10:03especially within the game window
10:05ok so that's fine that's great now we need to figure out where our cursor is
10:10in relation to our map array and all of those note objects that we just made so
10:15we'll do that with created x equals floor
10:20x / grid size and grid
10:25y equals floor y / that's not a shift grid size
10:34it's earlier than I don't really record my I'm just having the worst time with
10:38my four fingers they're not cooperating at all
10:40ok so all Florida's just real quick
10:43is that it just chops off whatever's after a decimal just get rid of it
10:46entirely so if this return like 3.17 level maybe whatever 33 done
10:52easy so now we need to go about updating our hover note information and like I
10:57mentioned earlier that's going to be with whichever node are cursors hovering
11:02over
11:03so first we need to put in a bit of input validation because it's possible
11:08especially people running on full screen
11:11if they try to go off their screen to like the left or the right
11:14wherever their second monitor is it could throw an air because the cursor is
11:19trying to find information way over on the other screen and that's obviously
11:23where
11:23the bounds of our map array so we need to be able to have it only update the
11:28hover note information if it's within bounds
11:31so if grid x is less than zero or grid of y is less than zero
11:40like I said earlier or works as a keyword if you can't find this silly
11:46icon on your keyboard or grid X is greater than or equal to room with /
11:55grid size or grid y is greater than or equal to room with room height / grid
12:08size i almost had myself an error on this very to build a game from the
12:12ground up just to be easy to program like in hindsight I should have made our
12:17grid perfectly square because then like we could just have room size or or
12:25whatever that thing was in the other thing that the map size rather than
12:29dealing with this silly stuff all the time so all right so if all that's true
12:40were in bounds and if we're in bounds
12:43don't know if if any of that is true then we're out of bounds confusing
12:46myself
12:47so if you're out of bounds hover note equals no one easy right
12:51else so if that is if all of that is false then we're in bounds and then
12:57hover node equals map grid
13:02x
13:03grid why great all right I will leave this here for now
13:08this is good this should be good let's double-check it so if we're less than 0
13:12and are these and if we r greater than room with divided record size + 4 y is
13:17greater than room height / grid size
13:22why does this look wrong to me this should be
13:27whoa don't do that
13:30what I didn't want to press to do that room underscore with / grid size right
13:35in this very room underscore height / current size right
13:41that looks right doesn't it that's got to be right
13:44we're going to go with that ok however note equals no one if any of that is
13:48true if all of that is false then we have a happier note green check out of
13:52here and now finally we're going to do a trial event this was going to get a
13:59little weird
14:02just a heads up so first thing we're going to do in here is if hover node
14:09doesn't equal no one draw sprite
14:19s selected negative one grid
14:25x times criticized grid
14:30y times grid size
14:36how did I manage that forehead
14:40sighs there we go ok so what this expects is a sprite
14:45obviously this is whichever some of it you wanted to draw if you set it at
14:49negative 1 then this will animate and the same schedule as whatever object
14:55you're making is and i'll explain that a bit just a second and then it won't say
14:59x and y coordinate and obviously that will draw it right where we want it but
15:03this is a proper spacing to make it look to the rest of the style that I'm doing
15:09things in soho I'm changing that now we wanted to draw itself
15:13ok so
15:16this is why we have a cursor that has four identical set of images in it right
15:21now while it's animating through those four identical images
15:27it will animate whatever else you wanted to draw on the same schedule in this
15:30case it will animate the selection box on top of the grid were hovering over
15:34now there's a bunch of ways you could have done this you could have set up
15:37some sort of internal step count for the animation or you can even create another
15:40object for the selected sprite just have that sort of move around but i think
15:45this is super easy and as far as efficiency is concerned adding three
15:49extra sprite images to our cursor is about as low impact as it gets
15:53if it's stupid but it works then it isn't stupid right
15:57and really you could give the curse of the same pulsing animation you gave the
16:00selector if you wanted to make it fancy or maybe maybe bike cycling between
16:04frightened dark red or whatever we're going to do one more thing while we're
16:09in here so let's have the cursor show us the grid coordinates of the note that
16:13we're hovering over will draw it just in the top left corner of our room a twin
16:17sisters one more thing we can mouse-over make sure everything's working right so
16:21we're going to do here is if i ever knowed doesn't equal no one and well two
16:29things real quick so first off as i mentioned earlier
16:32no one returns negative 1 and since instance ideas are always numbers
16:37there's something like 1000 15 or something silly like that we could
16:42hypothetically do if hover node is greater than 0 because then we'll ok
16:47well we know it must be an instance ID and if it's no one it will be less than
16:51zero so there you go you could do that but i like doing doesn't equal no one
16:57it's a bit sloppier it's not as neat looking or or cool from cool programming
17:03guys like to make their code look fancy
17:05um but you know what in a month's time I'll be able to read this i look at this
17:09would be like all right I know what this is trying to do and that's super
17:12important
17:13so the other thing we need to talk about is what's up with this we've got if
17:17however doesn't equal no one up here
17:19if however no doesn't equal no one down here
17:22why are we doing this why don't we just put them in the same thing
17:26well that's simple why we're doing these similar if statements here and doing
17:32everything in this order is because they draw vent runs top to bottom and will
17:35layer the sprites over the top of one another as it goes
17:38so top to bottom wanted to draw the selected sprite and then we want to draw
17:42our cursor on top of that and then we want this on top of everything else
17:47because it's just important information in the top left of your screen
17:50so that's why we're doing it like this what we're going to do here is we're
17:55going to draw a nice black rectangle with some white text and all its gonna
17:58do is say let's see
18:00temp text aight walls string
18:04whoa student space that string grid x plus quotes space / space quotes plus
18:14string grid
18:17why ok so string all it does is it turns numbers into letters so that it can be
18:24stored in a string or be displayed as a string or whatever so we're taking our
18:30grid X and we're taking our great why
18:33and then you can add two strings by just using the plus symbol and then quote so
18:38it's going to be
18:39let's say it's 3 + space / space plus 4 and that's what will display the top
18:47left corner
18:48easy i talked way too much about that that's really not that complicated you
18:50guys are sharp so now we want a black rectangle so draw set color see
18:55underscore black draw a rectangle
19:0000 string with temp text string height temp text and then that's not done yet
19:14we will also need a false
19:16ok so these are just x and y-coordinates of 00
19:21I string with returns the width in pixels of a string really handy string
19:29height returns the height in pixels of a string so we're going from 0 to
19:33however long our texts are string is and then from 0 to our however high our text
19:39is
19:40and then the last thing we put in here is an outline right
19:43we want false we don't want an outline we want to full filled in rectangle if
19:48you just want to get out line you could do true there and then draw set color
19:55see underscore white that draw text 0 0 temp text great real quick let's talk
20:09about this draught texts
20:10third row set color stuff because that's important so when you do draw setcolor
20:15you're not setting the color for this object you're setting the color for all
20:21objects
20:22so if you want the default is the underscore white that's what it comes
20:26straight out the box and that's what makes all of our sprites look like they
20:30look up here so if you ever set draw setcolor away from white for some reason
20:36do whatever it is you need to do and then set it back to white when you're
20:40done otherwise it will draw every sprite like with like if I if I forgot this
20:46fight if i didn't have that there it would draw every sprite like with a
20:50black tinge in the hell would actually probably make our whole screen black
20:53because we have a lot of white going on here
20:56so there you go always set it back to Center is quite coincidentally we wanted
21:00to set it back to white anyway but it's worth talking about
21:03so now the last thing we need to do is we need to create our cursor screen
21:08check out here and here and we could just throw a copy of it
21:12in the room but I don't like doing that right our cursor sort of married to our
21:18game object because it relies on a lot of information that that game is
21:24creating so like we can't have our cursor trying to figure out what's in
21:28the the map array before the mapper is to create created
21:31we can't have a try to figure out any of that sort of stuff right that would that
21:35would break it
21:36so what we need to do is we're going to create it once it's done with all of
21:43that
21:43that way we can be sure it doesn't throw in the air so right here at the bottom
21:46of our games creative and we want instance create a mouse and score x
21:53mouse underscore y ou cursor
21:57actually you know what it doesn't matter to heck with it x y ou cursor
22:02I'm pretty sure it won't because I mean hypothetically tried to create it
22:07outside the room like let's say you're on
22:10you had your he told the game to run and you put your mouth on your second
22:13monitor
22:14I don't think that would throw an error but it doesn't matter we created because
22:17the very first step is going to update itself to where the mouse is anyway so
22:21deck with it
22:22put it wherever you want it doesn't matter alright so now the last thing we
22:28need to do is we want to hide the default cursor while were moused over
22:33our game window so green check out here and here we want to go to global game
22:39settings this little bit down here and then we're going to go to Windows
22:44Windows and then yet
22:46this display cursor thing get rid of it green check out here
22:51and finally we know our note objects have their neighbors sorted right we we
22:56watched that we we checked it we verified that everything is working so
23:00there's really no reason to have that draw vent
23:03so let's go to our node and let's just completely delete this draw at all
23:08together
23:09gone like I said earlier if it doesn't have a draw event it will draw this
23:13sprite when it comes time to draw things anyway so we don't need it anymore
23:17it's done green check out of here let's fire up our game and see what we got
23:22you do
23:26there we go great so there's our grid that's working fine you can see our
23:30little flashy cursor looking great and we can verify that's definitely 00
23:35how how wide is my game app right now looks like it's 31 wide and 23 tall
23:44okay great so . is usually mouse around a bit you see the numbers change the top
23:49left corner and her little show 0 for the left and top edges that's that's
23:53right
23:54if you've got all this working then we're ready to add a character or two in
23:57the next video
23:58I would a big for internet . again you're enjoying the tutorial series be
24:01sure to like or subscribe and you can follow me on twitter at sergeant indie
24:05feel free to leave a comment below if you're lost or something broke
24:09I'm more than happy to help you out thanks for watching everybody else see
24:12you next time
 

TheWaffle

Member
I loved this tutorial. I used it as the basis for my war game (in the WIP forum).
Even though I used a HEX grid, this method works great for mapping in general.
Excellent explanation on the use of linked lists in GM.

More talking needs to be done on view scaling and the use of views for
creating interacting menus (buttons) that can be used during game play.
I still haven't found a fool-proof method of scrolling one view while still using the buttons in the other view.
 
I loved this tutorial. I used it as the basis for my war game (in the WIP forum).
Even though I used a HEX grid, this method works great for mapping in general.
Excellent explanation on the use of linked lists in GM.

More talking needs to be done on view scaling and the use of views for
creating interacting menus (buttons) that can be used during game play.
I still haven't found a fool-proof method of scrolling one view while still using the buttons in the other view.
The easiest way I've found, and the method I'm currently using for my own game, is to give each button a cameraX and cameraY. These variables are essentially the offset from the view_xview and view_yview. Then every End Step have them update their position based on the view_xview, view_yview, and the couple of offsets you've given.
 

TheWaffle

Member
The method I am using is having the controls (buttons) in _view(1) and the map in _view(0) and adjust the render so the controls do not move ...
Since the controls do not move, managing them is simple. Managing the map requires checking the current _viewx(0) and y stuff to find the mouse offsets.
The real fun comes in managing the map scroll in Y. When trying to click the buttons on the top, the map tries to scroll up. By tweaking the (edge sensor code)
I try to prevent scrolling up IF the mouse if above the threshold. Naturally, this is sorta glitchy. Mostly the view(0) follows the mouse (automatic) except when
the threshold value is reached. Not sure how to perfect it, just thought this would be a great topic for a later tutorial :)) Perhaps we can compare notes later.


in mouse object
Code:
/// track mouse
x=mouse_x;
y=mouse_y;

grid_x=floor(x/GRID_SIZE);
mod_x = x mod GRID_SIZE;

// using hex grid so odd number grid_x have a slight y shift
if (grid_x & 1)==1
{
    grid_y=floor((y-YOFFSET)/GRID_SIZE);
    
}else{
    grid_y=floor(y/GRID_SIZE);
    
}

if grid_x<0 || grid_y<0 || grid_x>=global.mapWidth || grid_y>=global.mapHeight
{
    hover_node=noone;   
}else{
    hover_node=global.map[grid_x,grid_y];
}
and later in mouse code

Code:
// ........................ map scroll .................

    

if (instance_exists(obj_map) || instance_exists(obj_complete))
{
    if y<view_yview+10
    {
        view_object=noone;//disable scroll
    }else{
        view_object = obj_mouse;
    }
    if view_yview < 64
        {view_yview=64;}
    if view_xview < 64
        {view_xview=64;}
    if view_xview+view_wview > room_width -64
        {view_xview=room_width -64 - view_wview;}
    if view_yview+view_hview > room_height -64
        {view_yview=room_height -64 - view_hview;}
}
 
R

Ramaraunt

Guest
Thanks for this series. I used it to pretty much master a* on my own.
 

JackTurbo

Member
Heyy, your tutorial was very interesting, but i had a problem, Why did mine only draws only on the top? Please Help
Check the double for loop in oGame firany errors (the one that creates the grid of oNodes).

Should either be in the create event or the first stage of the state machine in the step event.
 
Last edited:
P

+Prince.Skylar13

Guest
Check the double for loop in oGame firany errors (the one that creates the grid of oNodes).

Should either be in the create event or the first stage of the state machine in the step event.
I'm still on the first part, it's really frustating.. can't solve..
 
Top