 Ladies and gentlemen, boys and girls, welcome back to another PyGame Python tutorial. I'm Ruder the Null, I'll be your host this evening, hope you enjoy the fine wine and the refreshments. Now we're going to be talking about collision resolution. In the last video we were checking out collision detection and working with mouse movement and using the mouse to move around and therefore determining collisions. So in this video we're going to take it to a different direction. I'm going to be introducing collision resolution at least with rectangles like with these two blocks but unfortunately I'm not going to be using the mouse motion like I am before. I'm going to try and kind of reroute or kind of tinker the program so that we'll be switching over to sort of platform movement or at least up, down, left and right and that sort of things. So I'm going to be using the arrow keys to control rather than the mouse and we're going to implement that right now. The way that I'm going to set that up is actually by introducing two new variables to our player object or not so much our player object but the block that we're controlling. I'm going to set that up with h speed and we're going to be able to make control h speed and v speed for horizontal speed and vertical speed for just one instance which in this case that we've been working with it's been a block. So let's set that up. In the constructor that's where I'm going to set the self.h speed that's going to equal zero along with v speed that's horizontal speed and vertical speed. I'm going to define a function that will actually allow us to kind of manipulate that. Pretty easy, pretty simple. I'm stuttering. Change speed. Of course we need the self keyword and we want a h speed and a v speed that we'll be working with. All that we're doing in this function is very very simple, very very easy. We're just kind of adding the h speed or v speed to whatever we have. This is going to make for some pretty smooth movement if we just simply add it to what we have. That way in case we press multiple keys it will be able to recognize and kind of do with it appropriately what buttons we're pressing and when. I'm looking at my notes over here. I think the next thing we can do is go ahead and change this functionality in the event loop. That's where most of this good stuff is going to be happening. We need to be able to check or at least test, sorry for key presses and key left, key right, that sort of thing. We're not going to be looking at mouse motion anymore nor are we going to be looking at mouse button down. We are however going to be looking at key types that are key type down. This is a nested if statement. I'm actually going to create a whole new event type testing if it's key down. Then we'll test what the keys actually are event.key. You guys know how to do this. Now we'll start with our constants for left and right movements up and down in the screen. We're going to be using our a block object. Remember we created that right up here. It's being drawn on the screen every second because of our block group draw function. We're going to want to change the speed. If we're moving to the left, the way that that works in the pie game window. I shouldn't try to run this. I don't know why I did that. Oh cool, that actually works. To the left is this way, obviously left. Pie game reads its grid at 00, the x and y coordinates at the top left. The x coordinate and the y coordinate at the bottom right are the immense width and height of your window. In this case, 640 by 480, 00 is over here, 640 and 480 is over here. It's increasing as it goes to the right, increasing as it's going down, which means it's decreasing going to the left and decreasing going up. So that we're going to have negative numbers when we're going to the left and when we're going up. That's exactly why I wanted to tell you that because for the change speed function for the left key press, we have to have a negative number. I'll use negative 5. I should probably set up a constant for what this actually is, but easy enough, negative 5, we're okay with it. A player or a block should have its own speed so it can move at that speed and that way we can quickly change it without having to change multiple lines in the code. But for now, again, this is just fine for what we need. Remember, we need to pass in what the vertical speed will be. In this case, we're going to add nothing to it. We're going to change nothing. And then we'll go over to key right. Go positive. Let's go up and down. Up, remember, will be negative. Down will be positive. And that's changing the y-axis. Now we're actually going to change this for key up. Copy and paste all the code that we just wrote. And it's very, very simple. All we're doing is changing the value of what we're adding to either negative or positive, depending on what it was before. Just change the size. Just kind of negate it. Just use the opposite of what you had before. Okay, so now we actually need to be able to move the object. We've added in, of course, for all of this handling. And if I press the arrow keys, you see nothing is going to happen. I might be able to actually set, what am I thinking? I should set a message every time the arrow keys being pressed, but that might take too long. You know what I'm going to do? I'm just going to actually print out. I'm going to print out with the, whenever we receive an event, let's print out the A blocks, H speed, and V speed. This is totally not important. This is just a visual so that you can see that yes, we are actually changing the events. If I press the arrow keys, oh, you can't even see it because my window is too far up, but you can see negative five, five, zero, whenever I actually press the buttons. Okay, so now that we have that set up, we need to give the functionality to the player. And we're going to do that in a function called update. If you remember in the documentation, at least for Sprite, there has always been a Sprite.SpriteUpdate function. It methods to control Sprite behavior. By default, this does nothing, but we're going to be able to use it whenever we want. We can kind of implement it and make it work the way we want it to. I'm going to get back to my code and let's go write that in the block object. Okay, we've got the definition and let's keep moving, keep moving along here. This is where we're going to want to actually change the position of the player. I'm going to keep the set position function because we use that to actually put the player or the block on the screen at runtime, and I'm actually not going to put it at zero. I'm not going to put it at the center of the screen anymore because in that case, it's already colliding with the other block. I'm going to move that to zero, zero, just at the very top of the screen. I'm going to keep the set position function like I was saying, but I'm kind of going to use the same functionality by using the self set rect, changing the position of it by using the rectangle properties. We're actually just going to go ahead and add on whatever the horizontal or vertical speed is in the case of movement. That's good. Let's go ahead and back to our event loop down here. Any position in here should be fine. I think I'll put it where I want to put it. A block.update probably after we fill the screen, of course. A block.update. Now let me run this. If I bring it over, you can see my block in the corner. I can move that with the arrow keys. Even if we move over to the block over here, there is a collision because we still have our collision detection, but let's actually resolve this collision so we can't go through the red block. We won't be able to move past it or through it. It'll be like a solid object that we're literally colliding against. Let's add that functionality. We just created the update function, and that's exactly what we're going to keep track of the collisions. So we're actually going to be able to pass in a list of collidable objects or things that we can actually collide with. Let's set that up. Let's add a new argument called collidable, and that's going to be a group, a group of sprites, things that we can actually collide with. Now, in between the x and the y, the change in the x and y whenever we're adding the speed, we're going to want to actually set up a collision list, and this is going to be a list of all the sprites that we're actually colliding with. Now, how do we get that list? Well, Pi Game is fantastic, and they've got a function inside the Sprite submodule called Sprite Collide. Let's check that out in the documentation. Sprite Collide. Find sprites in a group that intersect another sprite. Okay, so here are the arguments. These are the sprites that we're actually looking for, or at least the sprite, this is the sprite that's testing whether or not it's colliding with any of the others in a specific group. So in this case, we want to test if this object, if this block is colliding with any others, so that's going to be self. Next, we want to know the group that we're actually testing in, and that's going to be collidable, or the list of collidable objects that we could potentially collide with. And then we continue Do Kill. It says this Do Kill argument is a Boolean value. If it's set to true, all sprites that collide will be removed from the group. Well, we don't really want to remove the objects, so let's go ahead and set that to false. And collided, what is this? Collided is a callback function used to calculate two sprites are colliding. It should take two sprites as values and return a Boolean value indicating if they're colliding. Collided is not passed, all sprites will have a rectify. Okay, okay, so that's totally optional, and it's not necessary for us yet. When we get into circular collision and circles in the game, then maybe we'll want to look at that function and see what more we can do with it. For now, let's leave that as just the way it is. So now we've got the collision list, and we want to check through everything that we've collided with and see which way that we're kind of colliding up against it. Because if we're coming from the top, we want to stop. If we're coming from the right, we want to stop. If we're coming from the left, we need to stop, and that sort of thing. So the way that we do this is actually going through the list. For each collided object, everything that we've actually hit in the collision list, we want to be testing whether or not we've hit it. And first, we're going to do that with the x direction horizontally. We set that up by testing if our speed, horizontal speed, is for one thing greater than zero. Because if it's greater than zero, like I showed you guys earlier in that visual demonstration, it's moving to the right, because it increases as the grid goes from left to right. If it's greater than zero, if it's positive, it's moving in the right direction. So in that case, we want some special values. We want to be able to check that the self.rect, the actual position, the rectangular position and properties of this object, if the right side of it, remember in the documentation, we saw that for the rect object, there is some properties like x and y, top, left, bottom, and right. These are the ones we're going to be using. And thankfully Pi Game keeps track of them for us. So if the self.rect right object is moving in the right direction, then we want to set that to the left of the object that we just hit. So essentially, we stop moving. Collided object. We want its rect, because that's going to be a sprite, obviously. The left side of that. So the right becomes the left and they stop. You can visualize this when the code is done and it's very visible. And then we will go ahead and keep moving. We want to test also the other direction. So this is going to be an L if statement because it's the opposite, but we're still testing if h speed is less than zero. Because if it's equal to zero, we don't care. It's nothing that we should be worried about because essentially, that means it's not moving. But we need to be testing if it's greater than zero, moving in the right direction. You should actually make a comment of that. And then if it's going in the left direction or if it's negative, then we do the exact opposite. We set the left of our object to the right of the other object. All right. That works just great for us. Now we do the exact same thing, but for the vertical section, the y-axis. Since we have already moved our position, since we've actually changed the left or the right side of the object, we have essentially moved and we should recheck for the collisions. So we're going to want to run this collision list again. Then we set up the exact same... Oh, sorry. I had a collision left over here as a syntax error. It's a good thing we noted that. Copy this for loop and put it again here at the bottom. And now we're testing, like we said earlier, for the v speed in the vertical direction, up and down. If it's going to be positive, remember that's going down. If it's negative, remember that's going up. And rather than working with the right and the left, we're going to be working with the bottom and the top. Going down, that means that the bottom is going to have to be stuck at the top. And if we're moving up, that means that the top is going to have to be going to the bottom. And that's the functionality that we're building. Okay. Cool. So now, we've got an object that could potentially collide with things. And it uses an update function with the list of collidable objects. So, let's see how we can finally implement this. Before we start our event loop, we want an object that is a group of sprites that we can actually collide with. Collidable objects, that's going to equal pygame.sprite.group. We want a group of these things. And then we'll go ahead and add that red block that we have, another block, the object that we're using in our program, to the list, or to the group block. Great. So now in the code, let's go back down to the update function, and let's pass in the collidable objects group. Now, I think we're ready. Let's run the code. Hopefully no bugs, hopefully no errors. All right, we got it. So I can move around, and now if I move over and come in contact with it, I stop, I stop moving. Yes. And I can move left and right, while I'm still holding down the down button, but still not colliding. And the same thing happens with the left and the right. Does it happen from the bottom? It does, because we've got the exact same functionality. So we did it. We can set this block to obviously be any size. We can pass that back in because we set that up in our another block, block creation object. We can set this to, let's say, be 100 or 200 wide, maybe 100 tall. And that'll go in the very center because we have the offset. If I bring this up, we still have the same functionality with the collision. And let's even put a whole other object in here. Let's say more block, more block. This one can be blue. That can be 300 and 20, way down low. Let's put you down 200 pixels down from the center of the screen. And we actually need to add that to the group, so it'll be drawn on the screen. Let's add right at the start more block. So it's essentially under everything. Okay. Now when you're in the program, we've got our more block down here. And you guys should know, here I'll make it so you can see the more block, you guys should know that because this more block block is not added to the collidable objects, we can go right through it. But the moment we add it to the collidable objects list, we're not going to be able to do that. So at the beginning, we can set up collidable objects, add more block, run the program. I move down and I cannot collide with this block anymore. We set up a collision detection for this block. Awesome. We did it, guys. We came so far. Thank you for watching, guys. I know this has been a long video. I hope you understand everything that I'm talking about. If you're coming from the left side, if you're moving in the left direction, then the left side of your object should become the right side of the other object. And the same thing happens for moving up. The top of your objects becomes the bottom of the other and the same functionality for all the other edges around this rectangular object. That is the best part of Pi Game with collision detection. And, man, we mastered it. We finally got it to work. Okay. Thank you for watching, guys. I hope you can understand this. Hopefully pretty soon we'll be looking at mouse movement and how to have collisions by using the mouse to move your player. But for now, this works wonderfully. Maybe pretty soon we'll get into gravity and start to add a little bit of a player functionality to a platforming game. Thanks for watching, guys. Hope you enjoyed the tutorial. I know it's been long. If you liked the video, please like the video. And if you really like me and what I do, subscribe. That'd be flattering and that would help a few more videos come to the table. All right. See you in the next tutorial.