Understanding Pygame masks

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

I don’t have time to watch it all but the intro at the beginning really helped me understand what masks are and how they are used. Thanks! I’ll check this out later.

👍︎︎ 1 👤︎︎ u/WhoWhyWhatWhenWhere 📅︎︎ Dec 04 2021 🗫︎ replies
Captions
hello in this tutorial you will be learning how to use mask and pie game and you can use that in a couple of different ways most importantly using a mask enables you to do pixel perfect collisions but you can also use a mask to fill a surface or give it an outline and if you push it a bit further you can also color in the overlapping areas between two surfaces and all of these projects are going to be what we will cover in this video which should be a reasonably extensive introduction to using a mask in pie game so let's get started and talk about what a mask is in the first place if you have used pygame even for a little bit you will know two objects surfaces and rectangles or rects and short his surface holds an image and is basically a rectangle with pixels and each pixel represents a color and a transparency and a surface does not contain any positioning information or any means to check collisions with another surface all you can really do is to change the pixels to make it look different and to get some basic information about it now if you do need positions and collisions you would use erect this object is basically a rectangle with points that you can move and measure and you can also check if a rectangle collides with another rectangle or overlaps with a point however you always work with rectangles so if you want to check for collisions between irregular shapes you would struggle quite a bit but for that we can use a mask and a mask is somewhat similar to a surface in the sense that it contains information for each pixel and uses that to draw a shape however each pixel only stores either 0 or 1 instead of a color information 0 means that the space for this pixel is empty and a 1 means that the pixel is occupied now i assume by itself this is a bit confusing so let's do this a bit more practical usually a mask is generated from a surface and in that process pygame goes through every single pixel of that surface and if a pixel is transparent then it will create a zero for this particular pixel however if any of these pixels has a color it will be a one in the math and that way we can tell which parts of the image are occupied and which ones aren't and that information can then be used in a couple of different ways the most common one is for collisions because you can check if one mask overlaps with another mask on a pixel by pixel bases what you can also do is to take a mask and convert it back to a surface and then color each of the pixels that way you can turn an entire surface into a single color or color specific parts of a surface like the surrounding area or any overlapping surface and that is basically all you need for the basic theory so let's get started with the easiest kind of a mask a collision mask inside of a sprite and alright here we are in the code and i've already set up a couple of things now if none of this makes any sense to you you probably want to check out my introduction to pygame it explains all of this in quite a bit of detail but ultimately what we're seeing in here isn't all that difficult and let's run it first and then i can talk about it so if i run this code you can see a white background a big alpha and a red rectangle and the red rectangle i can move around with my mouse but besides that really nothing is happening so let's close it and let's talk about the code now the really easy parts here this part this part and let's say this part down here because all of these are the super basic setup for pygame so if you just want to create a basic window this is what you would need now i guess on top of that in this line here i am filling the background with a white color so we have a white background color now the actually more interesting parts are these two classes up here and i'll talk about them in just a second but first of all be aware down here each of these classes is initiated inside of a group single so we have one group single for the player and one group single for the obstacle and it is important to keep these two in separate groups you're going to see in a second why but well each group is ever called player and obstacle and then in our game loop we are drawing both of them and the player also gets updated why only the player gets updated you will see in a second but these are the two classes we are going to need so let's have a look at them the first class let's look at the obstacle that's the easier one and in here you can see well that not much is happening we are inheriting from the sprite class so we're getting a sprite and then in there we are creating an image and a rectangle so the basics you need for a sprite and i guess the one thing in here is that for this class we are importing an image and this image is in the same folder but that still isn't anything particularly complicated so with that we can minimize this class and not worry about it anymore so let's have a look at the player and this one is slightly more complicated but not that much i guess first of all we're importing a sprite and for the image we are creating a plain surface and we are filling it with a red color and by default we are placing the center of this image at a position 300 and 300. so all of this is going to be very similar compared to the obstacle except now we don't import an image we are creating one in pi game itself now the slightly more interesting part for this class is this bit here and what this one does is it checks if there's any kind of mouse position and this is going to exist as soon as we are moving our mouse and once i have that i want to set the center of the rectangle to wherever the mouse is going to be and that way all of the stuff up here is always going to follow the mouse and this is also why we have to update the player group because only with that we are calling this method here so let me minimize it and now we can start to work on the collisions and let's do this in the game loop and let me add a comment let's call this collision now usually what you would do in pygame to get a collision with sprites is pygame dot sprite dot sprite collide and then here you have to pass in three different arguments you need a sprite you need a group and you need what is called do kill and let's go through all of them one by one now the sprite has to be a single sprite and in my case i want to look at my player so essentially this player here that i'm passing in there however this player right now is not a sprite it's a group that contains a sprite so this is a group as a consequence we couldn't use players straight away here we will get an error but we can fix that quite easily all we need is player dot sprite and that way we're going to get one sprite inside of this class now next up we need a group and this one is quite a bit easier because we already have our obstacle group and this actually is a group so this we can just pass in here so literally what i want to do is to get my obstacle and with that let's get rid of these lines now i need a ducal argument and all this really means is if this sprite collides with a sprite inside of this group do we want to delete the sprite inside of this group so this can either be true or it can be false in my case i do want this to be false because i don't want to delete the alpha inside of this group and all right with that we have a sprite collision method now what this method is going to return is a list with all of the sprites that are overlapping or well colliding so what we could do for example is use this in an if statement let's say i want to print the word collision and now if i run this and i move my red rectangle somewhere to the alpha we get collision in the bottom left however if you look very closely the red rectangle didn't actually touch the alpha itself at least not the alpha as we can see it and the problem here is that sprite collide by default uses the rectangle of the class so we are basically checking the rectangle around the alpha which usually works but sometimes especially in this case really does not work well and there's this huge amount of white area around the alpha that still checks collision so this is not going to work now fortunately we can fix that very easily there are two things we need essentially number one for each of the classes we have to set a mask so the player and the obstacle are going to need a mass and then besides that we have to add one extra argument into spread glide to check for a mask and not for rectangle so let's start with the first one and let's give each of these classes a mask and this is actually incredibly easy all you need is to add another attribute called self dot mask and this has to be called a mask and now we have to generate a mask from this surface here or well the image of the sprite class and this is also very easily done all we need is pygame dot mass dot from underscore surface and this method is going to need one argument and that is the surface we want to convert into a mask which in my case is self dot image so all i'm really doing is i'm getting this image and i'm passing it in here and then we are good to go actually and now we have to do the very same thing for the obstacle so literally all i have to do is copy all of this and now this obstacle also has a mask so now i can minimize these two classes again and now we come to the second step that spread collide needs another argument argument number four and this is basically to tell it that we are looking for the mask and not for the rectangle and for this one we actually have a couple of options if you check the documentation you can find all of them so we have collide rect collide direct ratio collide circle collide circle ratio and collide mask collide rect is the default one basically and we want to check for collide mask obviously so what we have to pass in here is pygame dot sprite dot collide underscore mask and with that we have a mask collision that's literally all we needed so now let's run this again and now look very closely my red rectangle is inside of the rectangle of the alpha but we cannot see collision in the bottom left however once i touch the black part so let me be very careful now we can see it that's really all there is to the most basic kind of mask collision now let me actually make this a bit more visual so it's better to see so if there's a collision i want to get myplayer.sprite.image.fill and fill it with a green color if there is a collision and if that is not the case so else i want to copy all of this and fill it with red this way all of this should be easier to see so now let's run this and i'm getting an error this double colon shouldn't be there so now let's try this again and there we go so now i can move the red rectangle and if we're touching the alpha it turns green but if we are not even though we are in the rectangle of the alpha it's red and this is working even inside of the alpha so here every time i touch the alpha we get a green rectangle and this is working remarkably well so all good with this one this is actually really workable cool so with that we have the most basic use of a mask and pie game and i guess the one thing you really have to be careful here let me open both of these classes is that if either of these classes were animated you would have to create a new mask after every time you are running the animation basically think of any time you're changing this surface here you want to create a new mask but that's about the only mechanical thing you really have to worry about although i guess the other thing you also want to worry about is performance because mask collisions are quite a bit more performance heavy than normal collisions or well rectangle collisions because remember checking every single pixel inside of both sprites is quite a bit more difficult than just checking rectangles now there's one thing you can actually do to make all of this quite a bit more performant and that is that you first check a rectangle collision and only if you have a rectangle collision then you check a mask collision and this is really easily done all i have to do is copy all of this and create a new if statement with the normal sprite collision so no mask whatsoever and only if we have this kind of collision then i want to check a mask collision so this way you're only checking your mask collision if both of these sprites are very close to each other which especially if you have a lot of collisions is going to make your game run a lot faster but if i run this we still get the same result except now we are only checking a mass collision if the rectangle is roughly in this area here but if we're outside of it we're not checking it at all so this is going to help our performance quite a bit and all right so this is then the first project in a mask and this is probably how you want to use it in most games because it's really easy and incredibly useful but you can also use a mask much more flexibly so for the next project we are going to do the same thing but we are going to do it without a sprite class and i guess let's jump straight into the project and let's have a look at this and all right here we have a new project and if i run all of this we can again see the alpha and we have our red rectangle that we can move around with our mouse so let's first talk about how to set this up and then we can talk about the collisions now guys at the top we're importing the normal stuff and then we are setting up the basic things for pygame and we have one section where we are creating the player so we have a player surface that we are filling with a red color and then we have a player rectangle that by default is in this position although this position doesn't really matter because further down here as soon as we have a mouse position we are setting the center of this rectangle to wherever the mouse is going to be and this is very similar to the class we have just seen and once we have the rectangle and the surface we are splitting the player where the mouse is going to be now besides that we also have our obstacle down here and this one is just going to be a surface and we have a position so there isn't even a rectangle and all of this we are blitting in this line of code in our game loop and i guess besides that we're also filling the entire thing with a white background color we have our event loop and let me close all of this and all the way at the bottom we have the usual updating and our clock so this again it shouldn't be difficult if you have even the basic understanding of pygame if you don't i would really recommend to check out my basic pie game introduction now let's talk about collisions and in here if you wanted to do normal rectangle collisions you will basically create a rectangle for this obstacle but in this case we are not going to do that for the simple reason that we want to have mass collisions so the first thing we have to do is to create a mask for each of our surfaces and let's call it player underscore mask and i also want to have an obstacle underscore mask and this mask will be created in exactly the same way that you have seen in this bright part so this could actually be a good exercise have a look at the sprite part and see how we created the mask there and now try to do the same thing by creating a mask from the player's surface and from the obstacle surface as well all right let's do it together now so to create a mask from a surface we need pygame dot mask dot from underscore surface and in here we have one argument that in this case is going to be the player surface and then we can copy all of this for the obstacle and now this has to be the obstacle surface and now for run this we should not be seeing any difference and looks good i guess the one thing to look for here is if we're getting an error message which we don't so that's a good sign so now that we have our mask we can actually in our game loop look at the collisions and again let me add a new section here so collision and outside of the sprite class you essentially have two ways to check for a collision with a mask one is called overlap the other is called overlap area and both of these methods work in basically the same way the difference is in what they return an overlap basically returns one point if there's any point of overlap overlap area returns how big the overlap is so how many pixels are overlapping but let's start with overlap by itself and to trigger this again we need an if statement and we need one of the masks it doesn't really matter which one in my case i want to go with my player mask and then here we could have a call overlap or overlap area in my case i just want overlap and in here we have to pass in two arguments the first one is the other mass we want to check and the second one is called an offset and let's say for now if that is the case i want to print collision so let's figure out the parameters in here the mask is the easy part because all we really need is this obstacle mass up here and pass it in here as an argument literally all we have to do is pass in obstacle mask and we are good to go now then what is the offset and this is a slightly confusing part at times basically what pi game here wants is that we pass in an x and a y position so at the top left of this mask is in the same position as the top left of this mass why that is specifically necessary i have absolutely no idea but pygame does want it so let's add a couple more lines of code and let's fix this and i guess let's name this properly let's call it offset x and y so we have offset x and we have offset y and essentially what we are looking for in here is for example for x is how much of an x do we have to add to our player mask to put it in the same x position as our obstacle mask and let me actually draw what we are going to need here let's say in yellow we have our obstacle mask that looks like this and let me call it o and then besides that we have our player mask and let's place it somewhere here and let's call this one p basically what pie game wants to know is what is this distance here and this is going to be the offset for x and it wants to know what this distance here has to be and this is going to be offset y and then if you combine these two offsets you are getting to this position up here and that way the player mask and the obstacle mast have the same top left position and fortunately both of these points are fairly easy to get the first one we need is the top left position of the obstacle and this is stored up here in our obstacle pause and this is a tuple with x and y so let me copy this thing and in here i want the first item and then i want the second item so this is going to be x and y and from that i want to subtract my player rect dot left and my player act dot top and let's do a really quick example to illustrate how this is going to look so here again we have our obstacle and let's just focus on the x part now so this line here is position 100 and now let's place our player again somewhere down here we have our player and let's say the left of our player is at position let's say 300. so to place the left of our player to the left of the obstacle we would need this distance here and remember this number has to be negative because we want to move this player to this side and to get this number we would need 100 minus 300 which would be the same as negative 200 and this is exactly what we would get from this line here and then the same would be applying to the y position as well and i guess with that we literally have all we need let's actually try this and now if i just move close to the alpha we get nothing but if there's a collision we can see collision so this is working really well so this is then what you are going to need for a mask collision without a sprite class and i guess what we can also do is let's actually print what this thing is returning because that's kind of interesting so now let's run this and let's see what we get so now if i touch the alpha in the bottom right of my rectangle and there we go we get 49 and 49 so this point here although it does change to further we go down and basically what this means that our player mast is a mast that has 50 points on the x and on the y dimension exactly what we specified in the mask earlier so let me actually close this and go to the surface so in here we are creating a surface that is 50 by 50 pixels and then when we are using this surface to create the mask down here we are creating a mask with 50 by 50 pixels so our first pixel is 0 and 0 and our bottom right pixel is 49 and 49 and essentially what this method here returns is the first pixel on the mast that has a collision so right now if i'm hitting the alpha with just the bottom right like this we get 49 and 49 so the bottom right pixel of this mask however if i go to the other side and let me scroll down all the way here now if i hit this alpha with the top left we should expect 0 and 0 because that's the first pixel in the mask and if i do that we indeed get 0 and 0. so this is exactly what we get here so if you have some incredibly specific need to check for one specific pixel on your surface this is how you would do it but probably not particularly useful all the time but with that we have a basic collision with a mask without a sprite now there's another thing that we can do and let me comment all of this out but i am going to keep the offset for x and y because we are going to need it right now our overlap triggers as soon as we have a single point but sometimes you don't really want that so if you want a more forgiving collision mechanic where collision only triggers if you have 5 or maybe 10 pixels of overlap but then you wouldn't use overlap you would use overlap area and let's work with that so again i want my player mast dot overlap except now i want overlap area but besides that i will need the same arguments so those two let me pass it in here and let's say again just for start let's print collision if i were to run this we would get pretty much the same result so as soon as there's any kind of collision we get collision in the bottom left so this is still working but now what is interesting about this method here let me copy all of it and let's see what it returns so now let me run all of this again and now if i touch the alpha we can see we get 1 10 and then again 6 and this is telling us how many pixels have been overlapping between our player mask and our alpha mask or while our obstacle mask whatever i called it and if i go all the way over this we get 2500 and this is the number you get from the dimension of our player mask because this one is 50 times 50 pixels and if you multiply those two numbers you get 2 500. and all right so now really what we can do is check if this number let's say is greater or equal to let's say maybe ten so enough around this i can get a tiny offset but now as soon as i get something greater than 16 we are triggering the collision again but again if there's only a tiny overlap we do not trigger a collision and of course if you set this number to something greater than the pixels in the mask let's say in this case 2501 we will never see this if statement get triggered so now we don't get anything because there aren't that many pixels in the mask but it could do something a bit more reasonable let's say 100 and now i can't move a bit over the mast and it's a bit more forgiving so if that's your goal that's how you would do it so with that we have the second way to approach a mask now there is a third kind of overlap and that is called overlap mask and this one is going to return a mask with the overlapping pixels between our player mask and our obstacle mask and this is what you would be using to color in an overlapping area for example we're gonna see that later in this video actually but for now don't worry about it and instead let's use a mask to color in a surface or to give it an outline which is something you can do fairly easily with a mask essentially what we are going to do we are going to take the surface we already have then from this surface we are going to create a mask and then this mask we are going to convert back into a surface and this is essentially going to give us a black and white version of the surface we just created and then we are going to go for every single pixel of that new surface and change each pixel to a certain color now you might be asking why can't we go through the original surface and change the pixels there individually and the answer is actually quite simple in the original surface we probably have lots of different colors and this is making it quite difficult to separate the visible from the invisible pixels and the mask is going to fix that problem entirely you're going to see later on in this video what this is going to mean in practice but specifically in this part we're going to either fill a surface or we're going to give a surface an outline both of which can be done fairly easily with a mask so let's jump straight into the project and let's have a look at this so here again we have an incredibly simple setup let me run all of this and we can see our alpha and nothing else we don't even have to moving rectangle anymore because we don't need it so literally all we're doing right now is that we are importing an image and setting a position here and then down here we are blitting all of this and i guess we have a white background but that's just about it and the first thing we need is to create a mask and this is happening in the usual way so obstacle mask as a new variable and this one should be pygame dot mask dot from underscore surface and in here i again want my obstacle surface and now we have a mask by itself doesn't really do much but well we can use it and essentially let me add a comment here what i want to do now is to turn the mask back into a surface and i guess let's be a bit more consistent here let's give this one a comment as well i'm creating the obstacle essentially what i want to do in here i want to turn the obstacle mask back into a surface and let's call this new obstacle surface and to generate this one we first need our obstacle mask so the mask we have just created and now this mask has a method that is called to underscore surface and when we call it we don't need any arguments in here and now we have an entirely new surface so what we can do now is in our game loop i can copy this entire line with the obstacle surface and now i can blit this new surface and let's just see what happens now we can see something slightly weird so we can see that we can still see the alpha so all of this part here but now it's white and all of the other parts that used to be invisible are now black so all of this part with the pink color now and essentially what is happening if we're turning a mask back into a surface is that any pixels with a one so all of the white bits here are becoming a white part in the new surface and all the pixels with a zero are becoming black in the new surface so essentially what we are doing is we are turning all the visible pixels in our original surface into a white color and all the invisible pixels into a black color we are basically creating a silhouette from our original surface so now what we have to figure out is how to color in this thing and the first thing that we have to do is to get rid of the black color and this can be done quite easily all we need is the new obstacle surface and then the method set underscore color key and this needs an rgb tuple and the rgb tuple is going to specify which color in this surface should disappear and in my case that is pure black so 0 0 and 0. and now let's see what we get we get well a white color i guess the white background color isn't particularly helpful here let me change this to a gray so we can see a bit better what's happening and now if i run all of this again we can see a white alpha and this is this new obstacle so if i comment this out we can see the original let me move to the side so what we can see right now is the alpha of the original obstacle surface and right now we basically turned this surface into a white color so that's a pretty good start i suppose but now how can we change this to any color that we want because you probably don't want this to be white all the time you might be kind of tempted to go with something like new obstacle surface dot fill and then fill it with the color you want to go for let's say red in this case if i do this let's run this it's not going to work because the pixels that we have hidden here are just that they're hidden they're still there we just can't see them so if we are filling the entire surface these pixels are being filled in as well so this does not help so let me get rid of it because it really doesn't help us all that much what we actually have to do is a tiny bit more complex and let me explain it first essentially what we have to do is we have to go for every single pixel of this new obstacle surface and check if the color of that pixel is white and if that is the case we are going to change that pixel to a specific color and this we are going to do for every single pixel of the surface and we can quite easily identify the visible pixels because they are all white but in a normal surface with different colors this would be significantly more difficult and this is why we are using a mask in here i guess let's have a look at turning every single pixel of this new surface into a specific color so here i am back in the code and let's add a new section and let's call it filling in the surface with a color and now we have to figure out a couple of things the most important one is we have to figure out the size of our surface and this is well quite easily done we can get the size of our surface with new obstacle serve and get underscore size and this is going to return a width and a height and in our case this is what we can capture let's call it surf underscore w and serve underscore h for surface width and surface height and this information we can use to loop over every single point so for x in range surface with and then for y in range serv h and i guess just to illustrate what is happening here let's print all of this so print let's turn it into an f string where we have x let's do it like this so it's actually working let's call it x and we get y and let's run this and we basically get all the numbers from 0 and 0 then we increase the y all the way to 49 oh no this one is larger so we get significantly more numbers we go all the way to [Music] quite a bit there we go so the alpha is 599 pixels high and this is then going to be our first go through and once we have that we start with x being 1 and then we do the same thing so we literally go through every single pixel of the alpha surface so this is working quite well now this information i can use to check every single pixel of this alpha surface and this happens with if my new obstacle surface and then get at and all this method is doing is it looks at a certain pixel and then gives me the color and this one needs an x and a y coordinate and this is going to look at a single pixel in our surface and it's going to return a color argument now in my case i know the color argument it will return for the visible pixels will be 255 255 and 255 short for white color so in my case really all i need to do is pick one of those and check if those are different from zero so i know that this pixel is white and well if it's any other color i know it's supposed to be invisible and once i have that information i can get my new obstacle surface again and then run set at which kind of does the similar thing what geta does except now we don't get a color we are setting a color and it needs again it to build with the x and the y argument and besides that it also needs a color argument and in my case i just want to go with orange and now if i run all of this we get an orange alpha so this is working quite well and sadly the logic here does get a tiny bit more complex so i would recommend you to go over this a couple of times just to make sure you understand the logic but really all we're doing is that we are looking at every single pixel of this alpha and if the pixel is white we are turning it orange and if it's any other color so in this case black we are just going to ignore it and that way we can turn the new mast surface into any color that we want while also ignoring the background black and the reason why we couldn't use the original mask is essentially this bit here because for surface generated from a mask we know that this position is always going to be white but in any other surface with various colors we couldn't guarantee this so we would always have the risk of one pixel accidentally being the wrong color and then it becomes invisible which would look very strange and the mask just ignores all of this and this is why we are going to use it now with that we can color in an entire surface and this is going to work with pretty much any kind of surface so this is quite good so then let's talk about how to give a surface an outline and there are two ways of doing this one that is very easy but also not exactly great then there's a slightly more complex one that well works much better and i guess it's best to explain both of them straighten the code that should make the most sense alright so here i am back in the code and the first thing i want to do is to comment out all of this because we are not going to need it for now but later on we are going to need it so don't delete it just yet and then also in the game loop i want to comment out the screen blit so if you ignored the commented out parts we are basically back to square one where we only have our alpha surface although we still keep our obstacle mask and now let's start with the first way to create an outline and this happens straight in the game loop and let me add a section here and let's call it the simple way to create an outline from a mask so basically what we can do in here is that every single mask including our obstacle mask have one specific method that is called outline and well this gives us the outline it's well quite straightforward and let's have a look what it gives us so if we run out of this we get a whole bunch of points right now they don't really tell us all that much but basically these points if i draw them in are all the points that are surrounding this alpha so if i drew all of this it will keep on going all the way let me speed this up a tiny bit so these are all the surrounding points ignore this bit it shouldn't look more like this now importantly what you have to be aware of that it does not give us the point inside of the alpha we only get the purple points outside of the surface if there's a hole inside of the surface it's not going to work we're going to fix that later with a different method but well now that we have that really all we have to do let me close this all we have to do is go for every single one of these points and draw a specific pixel in this position so what i can do is add for loop with four point in my outline and in here i want to use pygame.draw.circle let me spell this properly as well that might help and this needs our screen so the surface we want to draw on then we need a color in my case it's going to be red and then we need an x and a y coordinate we are going to create those in just a second and then we need a size let's say in my case a 3. oh well it's a radius it's looking for here so now we have to create an x and the y coordinate so it's going to be x and it's going to be y i guess we haven't done an exercise in a while so what i want you guys to do is to figure out the x and the y coordinate so try playing around with the outline points and see how far you get it really shouldn't be all that difficult so the first thing we need is the actual point and this point right now is a tuple with x and y and we want the first one so the x point and then for y we want to do the very same thing except now we want the first one and i guess now let's actually try this and this is almost working so we do get an outline but it misses the offset and the reason for that is remember a mask does not have any positioning information so the top left of the mask is basically always zero and zero and the same for a surface actually so this obstacle surface is only in the middle of the screen because we have the obstacle position and this one is 100 and 100 and this 100 and 100 we basically have to add to these points here and this is also very easily done all we need is the obstacle position with zero and then i can copy all of this and set it to one so that both the mask outline and the surface have the same offset so now let's run this again and there we go we have an outline if you have very simple surfaces this is already enough for most purposes actually so if you need nothing fancy this should work by itself and you could even make this a bit more pronounced let's say by giving it a 10 although it might be a bit too much i guess it still works so now we can see it quite a bit more or if you set this to let's say a 2 or even a 1 now it is significantly more subtle although the obvious problem is let me write again actually that if you want to color in the inner part the outline would never be able to do that so for this we would need a different approach but this one isn't that much more difficult it's just a bit more writing intensive and let me explain in theory what we are going to do essentially we are going to use again our filled in surface so the one we created in the first part of this section and this new surface we are going to blit multiple times behind our original surface and we are going to blit it in every single direction so left right up and down and then top right bottom right bottom left and top left and that way we are going to create an outline that is filling every single hole so let's jump straight back into our code and let's have a look at this so here i'm back in what we created earlier and the first we want to do is to let's comment out all of this so we can't see it anymore so again we are back to our alpha now what i want to do is to get back this part and this part and i also want to blit all of this again so now if i run the code we get back our orange alpha but now i don't want to have this alpha after the obstacle surface i want to run this one before and now what we get is well we can't see it anymore because it's behind the alpha but now what we can do is to mess with the position and let me actually demonstrate how this is going to look like so i can set my obstacle position with zero and my obstacle position with one so by itself this is not going to make any difference so if i run this same result but now what i could be doing is to change this position and give it an offset let's say 10 and what we can see now is now we have a color to the right of the alpha all we have to do now is to apply this to every single of the eight sides we could possibly move in and well let me put this into a section to keep it simple so let's call it the complex way for a outline and in here since we are going to reuse this offset quite a bit i'm going to put this into a separate variable and let's call it offset and let's put it at 10. so we first have the offset to the right and i am incapable of spelling offset and now let me give a bit of space we have to copy this thing eight times so let me do the first four this one is moving to the right let me actually add a comment afterwards so we can see what's going on so this one is to the right and this one is going to be to the left so all we need for that is negative the offset and now if i run this we have a left and a right offset or well a kind of an outline and now we can copy this again and let's put this one at top now for this bit we don't need an offset in the x at all instead we need one for the y and now if i run this we can see we now have a top part although if you pay close attention you can see that we have this ugly bit and this ugly bit and these can only be fixed if we're moving the background to the top right and to the top left there's no fixing this if we just move up and left and right this is why we're using the other directions you can see it down here as well it looks really ugly there but we're going to fix those in a bit now before that we have to copy this thing one more time and now the offset is going to be plus because this one is the bottom and now if i run this we have a kind of working background and importantly here now we have a filled inside so this is covering every single hole inside of our surface so now what we have to do is to actually fix these bits here and this bit here and this is done by well continue going on and let me actually add proper white space here so now let's start this one with the top right now first of all since we're going up this has to be negative and since we're going to the right like in this part here we need this offset as well so let me add it here and now if i run this now you can see that we have fixed this part up here and i guess this part down here as well so now we can do the same thing for the bottom right and the bottom right is going to be a plus here so this is going to be the bottom right then we can do the same thing for the bottom left and this is just going to be negative for the x and finally we get the top left and for this we just need a negative offset for the y now if i want all of this we have a much nicer looking outline and i guess this one is a little bit thick so let's change to 10 to a four let's see how that looks yeah that is significantly better so this is a much nicer looking outline that you will probably want to rather use in the game or well depending on the complexity of your surface i guess and i guess you could simplify this with a smart for loop but in my case i'm not going to worry about it but it's something to keep in mind if you have an actual game but with that we have outlines and we have the ability to fill in the shape so that's another project done so we're almost finished there's one more project to go and this one is the most interesting one because for this one we're going to color in only the parts of a surface that are overlapping with another surface which is a really interesting effect for basically any kind of game and is a really good way to understand how a mask is going to work so let's talk about how this project is going to work and in the most basic sense it's essentially a combination of project 2 and project 3. so from project two we are going to get the overlap method except now we are going to use overlap mask and this one is going to give us the overlapping mask between well one mask and another and then from project number three we are going to turn this new mask into a surface and color every pixel in and that's literally all we are going to need it honestly isn't that much of a complicated project so let's jump straight into the code and let's have a look at this here i am back in the code and i already have a couple of things set up let's talk through them really quick it shouldn't take too long now as always here we have the usual setup now besides that we have the ship setup and the alpha setup and for the ship we have a surface a rectangle and a mask and be aware i already imported the mask this is the step we haven't done earlier now for the obstacle we have a surface a position and a mask and this is pretty much the same thing we had before so all of this should be fairly simple now then here we have the usual event loop and we are adding a background now a bit further down really all we are doing here is we are drawing the background then we are getting the mouse position and placing our ship and then we are updating everything so all of this should be pretty easy at this point with that we can get started to get all the stuff ready to actually get the mask first and let me add a new comment here and let's call this the new mask surface coloring i guess that makes sense i'm kind of bad at naming stuff but anyway now the first thing that we do have to check is if there's a connection between our ship and the obstacle and this happens again with an if statement so if ship mask dot overlap with our obstacle mask then we want to do stuff so let's call do stuff now there's one thing i did forget here and i hope you remember because what we need in here is in a tuple an x and a y and both of these should be an offset so we again have to get an offset one for x and one for y and let's see if you can remember what we have done in the second project so pause the video now and try to find these two offsets let's try together now and i guess let me write on both at the same time that should save a bit time now first of all we need to obstacle position and in here we need x or y i'm going to add those in a second and from those we are going to subtract the ship rect and then for the x position it's going to be left and zero so this is going to be x and for the y position it's obstacle position one and shipwrecked dot top so check out the earlier part to understand the logic here at this point it shouldn't be too difficult or at least i hope it won't be so now we can tell if there's a collision and let's just check if this is actually working just to make sure i didn't make a mistake like i did with the typing so now let's try this and we are getting a collision cool so this is already working nice start so now we know when there is a collision from this line here whenever this happens we want to create a new mask from the overlap between our ship and the obstacle and this i also want to store in a new variable and let's call it new mask and to get this one we again need our ship mask and then overlap but this time we are going to add mask and this is going to return the overlapping mask between these two objects here and it is going to need some arguments although the arguments are exactly the same that we have used before so i can just copy paste them and we are good to go so i guess what we can do now let's just print new mask and let's see what we get and now if i touch the alpha we get a mask that has this dimension and well we don't really have to worry about the specific details here but we know we are getting a mask so that's a pretty good sign and now we have to pick up the stuff we learned from the first project so we are going to turn this mask into a surface and then on this new surface we're going to go for every single pixel and color this pixel in a certain color so what we need first is let's call it new surface and this is going to be new mask dot 2 underscore surface and this does not need any arguments and all right now we have a surface and i guess what we can do now is to actually blit this new surface and this should happen inside of this if statement let me actually demonstrate so what i want to do now is screen dot blit and i want to get my new surface but now i am also going to need a position and ideally this should be a rectangle unfortunately i do because this ship rectangle here we can totally use in here because the new mask we are creating is going to be essentially the same size as the ship so let's actually try how this is going to look so we are just going to use the ship rectangle so now let's try how this is going to work and now if i touch the alpha the ship turns black but if my ship goes over the alpha we can see the silhouette of the ship so this is working really well and now what we can also do for this new surface we can set a color key so i want to get my new surface and then i want to set my color key and the color i want to get rid of is 0 0 and 0. so the color black and let's add a bit of space here and now let's try this again and now when my ship touches the alpha we can see that we are just getting a white color so this is already working really really well so that's a pretty good sign so now all we really have to figure out is how to change this white color to essentially any color that we want to have and this is what we learned in the third project so let me give a bit more white space and before i start i would really recommend you to try this yourself because if you look at the passcode implementing this really shouldn't be all that difficult and it would be really good practice i think so try this yourself and see how far you get first of all we need a surface width and a surface height and this we get from our new surface and then get underscore size and this is going to return a tuple that we need for the width and the height now next up i want to go for every single pixel in the width and the height so for x in range surface width and inside of there for y in range surface h and now inside of here i want to check if my new surface dot get at so i want to check one specific pixel inside of this new surface and this should be at a position x and y and if this is white it's going to be 255 255 and 255 as a tuple so i can literally pick any number from this and check if this is different from zero if it is we know there's a pixel if it isn't we know it's going to be invisible or well black so now once we have that all we have to do is get our new surface and then set underscore at get our x and y again and now all we have to do is to pick one specific color let's say my case let's go with red and now let's try all of this and you can already see it now this is working pretty well so with that we have a ship that shows a outline that we can only see if it overlaps with the alpha so really wasn't all that difficult to implement i think so this is actually working really well cool so i hope that was helpful and i will see you around
Info
Channel: Clear Code
Views: 3,555
Rating: 4.9786096 out of 5
Keywords:
Id: uW3Fhe-Vkx4
Channel Id: undefined
Length: 60min 29sec (3629 seconds)
Published: Sat Dec 04 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.