Hello fellow Scratchers! Do you like mazes? Ever wanted to generate your own?
I'm Griffpatch, and today we are going to learn how to do just that! Mazes are basically one long twisting corridor from start to finish, but with branching corridors with dead ends that confuse those attempting to solve them. So the question is, how do we get Scratch to draw one? Well, we'll begin by deleting this sprite one, and since we now have the stage sprite selected, let's go into the backdrop background editor.
and switch to the bitmap mode. Fill the entire screen with pure black. This will be the colour of our walls, and will carve out the maze from this blank space. Next we'll create a new sprite, naming it Bounds.
The sprite will define the maze bounds. We want to draw a non-filled rectangle with a pen width of something like 40 that's quite fat. And I'll draw it out such that it sits just inside the drawing canvas border.
like so. Once I'm happy with its size, I'll just reposition it on the stage by setting both its x and y position to 0. Now we'll add another new sprite, naming it Maze. This will be responsible for all the maze drawing, so what costumes will we need? The first we shall name Tile, and we'll need to be quite accurate drawing these costumes, so zoom right in with the plus button such that we can see only a handful of the checkered grid tiles.
Remember each grid tile is 4x4 pixels in size. I'm choosing my maze floor tiles to be pastel yellow. I'm selecting a transparent pen colour though.
And then we can draw our square that is exactly 8x8 pixels. That is 2 full grid squares wide and tall. Then to ensure it's centred, just drag it until it snaps in place. Next, we will draw the main costume that we need, and this is a corridor costume.
We'll duplicate the tile costume and name it corridor. Now, using the selection arrow tool, we select the yellow rectangle and then pull out the right hand side to make the rectangle 5 grid squares long, that will be about 20 pixels wide and 8 tall. Make sure not to centre the costume this time round.
as it wants to be just like this. Now to help us understand how the maze is being drawn, we'll add a thin red border to the corridor costume, and also add a small red arrow on the left hand side like this. Ok, now don't do this in your project, but let me just copy this rectangle to show you how we are expecting these costumes to fit together in a maze. You see, the start of one corridor will stamp directly.
over the end of the previous one, like this, when in this formation you can see that the length of a single path tile will actually be 3 grid squares, that is 12 pixels wide. We'll need that number shortly, so remember it, 12 pixels. I'll just delete these parts to tidy up, and we can look at how to actually script this. So, move into the code tab, and we'll begin by clicking on Extensions down here.
and adding the pen extension. We'll need this to draw the maze as we're definitely going to exceed the clone limit. Drag in the Erase All block. This is used to clear the screen of anything we've previously drawn.
We'll trigger this right away off a when green flag clicked hat block. So firstly, how big is our maze going to be? It'd be nice to have a way of setting this, so remove the my variable here and make a new one, naming it Tile Size.
for all sprites, and then set it to 40. That is, our tiles are going to be 40 pixels wide. That's nice and chunky for starters. Now bring in a set size 2 block.
Ok, so how are we going to size our sprite so that it is working at the selected 40 pixel size, where this size block expects a percentage? Hmm, tricky, but no problem. Let's do the math. Setting the size to 100% would mean our tiles are of size… what was it? 12 pixels.
Remember? That's what we said. So, if we divide 100 by 12, it will be 12 times smaller, which would make it 1 pixel wide rather than 12. Good.
From here it's easy to make it 40 pixels, we just simply multiply it by tile size. Aha! We win! Next.
Let's position the sprite where we want the maze to begin. Go to x , y and we'll point in a random direction. R, but we only want to point directly up, right, down or left.
Nothing in between. To do this we need the direction to be multiples of 90 degrees. So multiply 90 by picrandom 1 to 2. If I run this script you can see that this gives us the directions 90, 0, 90 and 180. Perfect.
Now we want this corridor to build up in length, so we'll make a new custom block. naming it Draw Maze. For now we'll leave the run without screen refresh clear, and we'll drag this new block to the end of the green flag script. So we want to draw using the corridor costume, so switch to that, and then right away we use the pen stamp block.
So with an imprint of the costume left behind on the stage, we can move the sprite forward by tile size, like so. Clicking the Draw Maze script over and over. demonstrates this working.
What will make it even better is if the corridor had bends. So, use a turn right block after we move. Either we turn 90 degrees left, or keep straight, or 90 degrees right. That's just 90 multiplied by random to 1. We can test again by clicking the green flag, and then repeatedly clicking the draw maze button.
Oh yes, this is looking really cool! Note that because we are stamping only the last corridor costume on the stage is actually a sprite. Right, enough clicking to build up this corridor, we need another draw maze block that we can drop in at the end of the existing draw maze script. This is now one of those dangerous recursive scripts I've mentioned before, since the block is making use of itself.
Be warned, if this was running without screen refresh it would reduce your computer to a crawl, so don't do that yet. What this maze needs first is is to respect its bounds. We'll stop it in its tracks using an IF here around the draw maze.
To allow it to continue to draw, the sprite must NOT be touching the bound sprite that's the rectangle around the level. If I run the project you can see that the corridor does indeed stop when it gets to the edge. Good. Now we also do not want the corridor to cross over itself, so extend the sensing check with an OR and check for touching colour. and select the corridor colour using the colour picker.
But, if we run the project we can see that we have a problem. The maze now stops right away. And why? Well, look, if I move the sprite a little out of the way you can see that the corridor is of course already touching the yellow from the previous corridor tile. We can't have that.
We'll need to add a special sensing costume that doesn't overlap the corridor tile we are coming from. Open the costumes tab, and duplicate the corridor costume. Select the rectangle, and carefully drag the left hand edge to the right until the corridor is 2x2 grid squares.
The arrow to the left shows where the previous corridor tile will end. This can be deleted. And then colour the block red, so that we can identify it from the corridor colour. We can name this costume, Detector.
Now click back into the code. and we'll switch to the detector costume before we perform the touching sensing in this if. I'm going to run the project again now, and we find that it's working once more, and also, there, we have reached a point where the corridor has collided with itself, just like in the game snake.
So obviously we don't actually want to stop when this happens. It would be much better if we could try a different direction to turn in instead. Well, At each point there are potentially 4 directions of travel.
We can add a repeat 4 around the if sensing condition, and we can turn right by 90 degrees as the last script in the repeat loop. So if we fail to move forwards, we can certainly now turn right and try the next direction. Let's give that a run. Oooh, now! What are we seeing here?
The corridor travels nicely winding around but it still appears to be running into trouble and stopping in a mad spin. Ok, well, the reason is simple sometimes the corridor goes head first into a dead end of its own making. In this case it doesn't matter how many times we turn around, there simply is nowhere else to go. So, how do we get out of this one?
The answer is, we don't. Instead, we need to back up until we find a place where we can begin drawing again. The difficulty is… how do we know where to back up to since we were randomly turning left and right as we went? Ok, For this we need some help from a custom block with input variables.
As unlike normal variables, these do not forget their values when we return to them. Let me show you what I mean. Make a new custom block naming it try directions from, and we'll add a numeric input, named start direction.
We can move the turn right script and everything following it into there, and replace with a call to this new try directions block, but… And here's the clever bit we pass in the current direction of the corridor. This is the direction before we have made any random turns. The magic of this is that we can point back to the start direction at the end of the script, guaranteeing us to be pointing the same way we were when we came in. This means we can now safely move backwards by tile width using a subtract block from 0 to backtrack us to the previous tile position. The magic of this idea is that even when we now reach a dead end, and all 4 turns come back blocked, we now reverse the turn that led us here, and move back to the previous position.
This in turn takes us back to the previous tri-direction script, and we'll continue turning around to see if there are any other directions that are yet to be travelled. This process is now fully recursive. and should leave no turn unchecked. Which means, running the project now yields a rather awesome sight! Wow, just look at that!
Did you see us backing up the pathway, and then finding a new possible turn there? And that's it! The whole screen has been filled, and the maze is complete!
The script ends with the maze sprite back at the beginning where it began, and nowhere else to search. Well, X marks the spot right, so… I'd suggest we swap out the corridor costume for a small x here to finish off the effect. Switch to the costume tab and we'll make a new costume naming it X.
Now, draw a sweet little x using a 3 pixel wide line. It should be around 6 to 7 pixels in size. Mine is just over the size of 1 grid square, and I'll select and drag it to ensure it's nicely centred. While we're here, we might as well click into the corridor sprite and remove the arrow and the red border from the costume. Now back in the scripts, simply switch costume to X after the drawing of the maze.
We can run the project again now and see a much cleaner maze form. And then to finish it off, there's the red X, right in the centre where we began. That is awesome!
So, being the naturally curious type of person you are, you may have already fiddled with the tile size variable we set up at the Well, if you haven't, now is the time! After all, the smaller we make the tiles, the more maze we can fit on the screen. Let's change tile size from 40 down to 30. And here we go I can see there's more to the maze! And it's working great! I have to say though, it's taking its time.
Perhaps we can make this run faster by editing the define draw maze block and making it run without screen refresh. So how fast will it draw the maze now? Click the green flag… Wow, did that finish already? I'm just clicking the green flag over and over.
I can't get enough of this… No wait, I can because now I want to set the tile size down to 20. Yikes, still fast! So, a tile size of 10 then? We have reached the limit of our sprite costumes. Now, you can play around with changing the costumes to bitmap, or adding a transparent rectangle around them, but to be honest, if you stick with the original, you can't do much. with a size no smaller than 12 you'll probably be safest for now.
However, to continue this tutorial let's back up the tile size to 20. Interestingly you may have noticed that the maze produced has a rather zig-zaggy pattern. This is caused by the random turning left and right with every move forward. Perhaps it would be nice to make corridors that have a preference for straightness. Here's a script that turns us left or right. So, if we didn't run this script then the corridor would be completely straight.
But obviously, we need to turn at some point. So, surround this turn with an IF and compare pick random 1 to 10 with 1. This means there is a 1 in 10 chance for the corridor to change direction. Ok, run the project.
Oh my gosh, I think we overdid it. These corridors are very long and very spirally. I rather like it, but let's tone it way back down we'll use a random 1-2 instead. There, that's a good balance, and it gives you another fun variable to tweak.
So let's take stock. We have a resizable maze and the centre is marked off with an x. What we lack perhaps is a start to the maze.
How about we find the longest possible passageway from the centre and stick the start there. To do that we'll need to keep track of how long each corridor is as we draw it. and simply remember the furthest point reached. We'll add 4 new variables to achieve this. Furthest x for all sprites, and furthest y also for all sprites, and then furthest distance for this sprite only, and distance for this sprite only.
We can set both furthest distance and distance to 0 before drawing the maze as we've yet to travel anywhere. it'll be easier to create a custom block to record the distances. Make a new block naming it recordDistance, then if distance is greater than the furthest distance travelled, then we set furthestDistance to distance.
Furthermore, we also record where we are when we recorded this distance by setting furthestX to X position and setting Furthest Y to Y position. Good. Now come over to the Draw Maze script.
Every time we move forward we change distance by 1, and when we move back we change distance by This will keep track of how far we've travelled from the start of the maze. Now we can pop in our record distance block just after we increase the distance variable here. If this is the furthest we have travelled so far, then it will record the position in furthest x and y.
That is all we need to find the furthest point. Now we just need a way to mark it on the screen. So let's add a broadcast to a new event named maze generated, after everything else has run in the green flag script. Create a new sprite named player.
and I'll draw a costume of a small blue circle. Mine is 7x7 pixels, and it sits nicely within the 2x2 grid cell. I'll make sure it's centred.
Now to the code. This is simple. When I receive maze generated, then using the of block in the sensing category, we set size to the size of maze. This keeps us the same size as the maze tiles. Next.
To position our player sprite at the furthest point from the centre of the maze, we go to furthest x and furthest y. Now I'm excited to run this because I love it when a simple script can solve a seemingly difficult problem. And there we go!
We are seeing the blue player dot being positioned at different places on the maze, and all looking very good. Yeah, this is really, really fun. I can feel my eyes trying to trace the route between the start and the end to see if it is the longest path. I guess it's this way. Then here… Woohoo!
I did it pretty good there! Well done me! Ok, so it's fun enough using my mouse, but it would be even more fun if we could control the player sprite using the keyboard.
Yeah, let's gamify this project! This tutorial isn't so much about player movement. Many of you will have lots of experience doing this part yourselves.
so don't feel you need to do it the same way as me feel free to use your favourite scripts for player movement. But I'll quickly code something up so we can get the job done. I'll start by hiding the player sprite when the green flag is clicked. Then, adding to the when I receive maze generated script, I'm going to wait until no keys are being pressed. This is to ensure that we don't move at the very start of the game until all the keys are released.
Now, we can show the player sprite before starting the game loop. repeating until we are touching the maze. Yeah, touching maze gives the wrong impression.
It doesn't really mean we are touching the maze at all. What we are touching is the X in the middle of the maze, as that was the last costume the maze sprite was changed to, and that's going to be our goal. Then, on touching the maze's X, we exit the repeat loop, hide the player, and broadcast a new message of reset maze. Next up, create a new custom block named move with two inputs of dx and dy.
run without screen refresh. To ensure our player's circular shape doesn't get caught on the edges of the maze, we can add a second costume, a small centred square, naming it square. And while I'm here, I'm going to name the circle as player. We can switch to the square costume and move the player sprite by dx and dy in the x and y directions. Now check if we are touching the colour black.
And if we are, we move back to where we came from, using 0, subtract dx, and the same for dy, like so. Finally, set the costume back to the player circle costume. Good. So, all we need to do is detect the player key presses right? I'm going to do this next bit in a slightly different way to usual, but nothing crazy.
We create a new block named move with speed, with an input called Speed, running without screen refresh. And what I'm going to do is rather than detect keys and then move in big steps, I'm going to repeat right here once for each pixel I want to move. Use a ceiling block to round our numbers up to the nearest whole number, and then pop speed in here.
Now comes the keypress scripts. Drop in two calls to the move block. The first is for moving left and right, and we'll use the trick of key right arrow pressed, subtract, key left arrow pressed to give us the 1 for right and minus 1 for left.
We can duplicate this and use it for the right hand input of up and down. I'll fill in 0s in the other inputs. So the trick here is that these moves are small, just a pixel at a time, but we loop the movements a number of times each frame to allow us to move further.
We just need to drop a move with speed block into our game loop over here and decide how far we can move each frame. It's a good idea to move relative to the size of the level. So perhaps we'll move by… tile size divided by 8. That will mean it takes 8 frames to move across each tile of the maze. You may want to play around with this value, or do things completely different.
It's up to you. Wow, we are almost done. We should just take care of this last broadcast to reset the maze. Click into the maze sprite, and add a when I receive reset maze hat block, and then move the entire script from under the green flag into there.
And we'll pop a broadcast to reset maze to trigger from the green flag. And by jove I think we've done it! Let's click that green flag and give it a test drive.
Ok, so the wall collisions are working great. I can't pass through them, and I'm not getting stuck on the edges or corners. I can move right up against the walls and still move around.
So far so good. I don't feel the maze is overly taxing yet, but you never know when you might have gone wrong when there are certain choices. Ah yes, I have gone wrong. Hold on, let me back up a bit. It must have been that turning back there.
And this turn… oh no, no, no, no, this way. Oh poop! No, back this way! Yeah, this is looking good… Ahhhh… No… Um… No, it's ok, here we go… Oh phew, ha!
We did it! And the game resets, and we have a new maze to solve! Wonderful! So, how about setting tile size down to 12?
That makes things even more tricky. Ha! You know what would be really good is perhaps a timer or something?
Well, I'll leave that up to you. Yeah, this maze is a lot more hard to predict. Let me speed this up. Yay, I got there in the end! Now, before we finish this tutorial, let me just show you one more cool thing you can do.
If we click into the bounds sprite and add some shapes to the costume, see how the maze shape now is bounded to fit around these shapes. That's really cool! It's even better in black.
Look, we can do circles! or perhaps letters. Yeah, I love this.
I know you guys are gonna have a lot of fun with this too. And with that, that brings us to the end of this tutorial. I hope you've enjoyed following along, and have made some excellent mazes of your own.
If you enjoyed this tutorial then smash the like button, and don't forget to subscribe to the channel to avoid missing my next exciting video. Thanks for watching, and Scratch on guys!