 Okay, it's been a real long time since I've done anything with Pygame, or really haven't done much with Python in a while in any of my videos. I've just been working on other stuff, but I did have a viewer request that I do a tutorial on Pygame on classes and objects, which is something I don't think I ever got into in my series. I only played with a little bit myself. First, my response was, oh, that's not on my list of things to do, but I thought it'd be fun. Don't expect me to do a whole bunch of videos right now on this subject. I'll do this one and maybe one more in the next couple of weeks. But I thought I'd show you the basics. And like I said, I'm very rusty with my Python. I have some notes in front of me here, but let's just make, I'm going to make a little script here that makes little bouncing ball particles. I'm going to fake some physics. You know, I don't know much about physics programming. I've used Blender before, which does all the physics stuff for me, but I kind of fake it in this program here. So this is a script I made a long time ago, but never did a tutorial on, so let's get started. Vim, I'll just name my script go.py, but you can obviously name it wherever you want. Also I'm using Vim as my text editor as normal, but feel free to use whatever text editor you prefer. Once again, we always start our scripts off with our shebang line. This just lets our operating system know what environment, what interpreter to use to interpret the script. Otherwise it just sees it as a text file and will try to run it as whatever your default shell is unless you call Python each time, which can be a little annoying. But putting the shebang in there gives you the option either way to do that. Okay, so we need to import some modules. So we're going to say import, obviously PyGame, system, and then we're going to want some random information when we're generating stuff, so we're just going to say random. So we're importing three modules, and then we're going to say from PyGame.locals, I'm going to say import all, which some people say not to do. And if you're being real strict about stuff, which you should, this is something you shouldn't do. Basically, I'm importing more stuff than I need to. It's a little sloppy, but it gets the job done. That's just how I know how to do stuff. Next I'm going to set a couple variables. I'm going to set height and width. This will be the height and width of my screen, the display of my game. I'm just going to say 800 by 400 gets kind of a wide screen. I'm also going to set a variable called z, which we'll have to do with the mouse later on. And you'll see when I do the mouse clicks, I know that I learned a better way of doing it, and I can't find my notes on that. So what it will do here will work, but not necessarily the best option, but you'll see. So next we're going to create our screen objects. We're going to create an object, screen, and we're going to set equal to pi game dot display, display dot set mode. And then we're going to throw in our variables of W and H. So what we're doing here is we're creating an object, and we're going to be using the pi game module. From within that, we're going to be using the display object, and we're going to, while we're creating it, we're also going to set the mode for the height and the width, which we created in variables up here, whoops, in variables up here. Now you could just put those numbers in here, but later on, if you need to call the height and width, it's easier just to call those variables. And if you change it later on, you just have to change the variable, not each instance of it. So once again, using variables is a good thing. We are going to say pi game dot display dot flip, which we'll get into more, we're going to do a few times in here, which is basically refreshing the little screen there. So at this point, I guess we can start creating our while loop, which is going to be our main loop. We're going to say while one. So this will be a loop at loops forever. We also would want to, before we start our loop, create an object called clock. Obviously, it's that side of the equal sign. You can call pretty much whatever you want, but we're going to do is we're going to say, look at the pi game module, look at the time function, look at the clock object. Okay. So we created the clock so that now within our loop, we can say clock dot tick, and we can give it a frame rate here. I'm going to say 60 frames a second. What that's going to do is going to limit the number of times this while loop can go per second. Now it could go slower if your computer's real slow, it may be running at 50, 30, 20, or 10 frames a second if the game starts getting very system hungry. But what this does is if you've ever played old DOS games and tried to play them on a new computer, sometimes things go super fast. That's because they didn't set limits back then. They assumed, well, we're just going to run this game as fast as you can. And on older hardware that worked. But on newer hardware it's so fast the game goes super fast. This is just like a speed limit. It prevents that from happening. So 10, 20 years from now when computers are five times faster than they are now, the game isn't super, super fast and unplayable, excuse me. Next we're going to set some variables here. I'm going to say pi game.event.get and, oh, I'm sorry, no, no, no, no. I'm getting ahead of myself. Pi game.mouse.getPOS. Now this will just, every time it loops, it will check the position of my cursor, the position of my mouse, which we'll need when we go to click on the screen and we want to know where the mouse is. Next we're going to say for event.type equals, remember it's two equal signs because we're not setting it, we're checking it. And we're going to say pi game.capital quit colon. And then we're going to say sys.exit. And what this is doing is when we create a pi game window, there's going to be a little x up in the corner to close it like most programs. But the script doesn't know what to do with that. Well, I mean, it will close the window but it won't close our script. So this sys.ex will say when the pi game window is closed, just kill the script. Once again, you may have multiple windows in your thing and you may not want that, you may just want the window to close, but we're just having a one window application so we want that there. Next we're going to say screen.fill. And I just realized something, once again, I'm a little rusty on my pi game stuff. We did flip up here. I actually think that's a little depreciated and actually update is the proper term to use now. We'll see, if I get an error then I'm wrong. So here we're just going to fill the background of the screen black. We're not going to use any images in the background in this game. We're just going to have the background be black. It's RGB. So this is the red, green, and blue value. 0 through 255 would be numbers put in there. So if you want it to be blue, RGB. So this last zero would be the blue. If you want it to be 100% solid blue, you leave the first two at zero. Put the last one there at 255 and you'll have a solid blue. If you want purple, you can then turn up the red a little bit and mixing the red and blue and you'll get a purple color. So just a little information on that that we're not going to use too much. But we're going to say display dot, I'm sorry, pi game dot display dot update. Once again, I think it's update. I used to use flip, but like I said, I think update is more correct. So let's look at our while loop here. It's going to loop continuously. The only time it's going to stop is when we tell it to stop, which would be when we do the sys x here, when we click on the x on the game. We're also limiting the speed of the game to 60 frames a second. We're constantly checking the mouse position. We're constantly after everything else updates. We're going to clear the screen. We're going to fill the whole entire thing, make it black. And then we're going to say update the screen, OK? So now that we have all that, let's just save it. Make it executable. Once again, we used our shebang line. So we can just make this executable. We only have to do that once and then run it like so. And I got an error here. Line 24, event type, syntax, I'm not sure why it's telling me that it's invalid. Let's have a quick look at my script here. Oh, I typed that all wrong. That would be why it is wrong. We're trying to check event here. I kind of merged two lines together. Once again, I'm very rusty at this. That's fine. Me making mistakes will help you notice mistakes in your code later on. That's what I always say. We were, I was saying four and then I went to an if statement without finishing my four statement basically. Pi game dot event dot get. So what we're doing here is we're saying four events in Pi game events. So basically is anytime there's an event in Pi game, we're going to create a variable object here called event. So it's going to constantly be checking that every time something happens in the Pi game, it's going to check this. Then we're going to say if that event equals Pi game quit, then sys exit. Save that, run it. And here's our window. That's what we've created so far. We've created a window. The only thing it does is when you click the X, it closes. So now we need to start creating our objects for the games. Now if you watched my previous tutorials, there was one where I made a space invader game and you would shoot some bullets out, but you were kind of limited on the number of bullets you shot and then it kind of like removed them. That's because I wasn't using objects. Basically I created a bullet, created a bullet. I think I did three of them. And then to create a new bullet, I was still using the same bullet just for you setting the position. What you were going to do in most cases when you're creating games, you're going to create objects. That way you don't have to write code for every single bad guy. You create a class, which is an object, and then you create an array for every bad guy in the game or every bullet in the game. In this case, we're going to create little colored balls that are going to bounce when you click. So let's come down here. And right about here, I'm going to create a class and I'll call it ball. And then every time you create a class like this, you're going to create a function. So we're going to define a function, underscore, underscore, initiate, underscore, underscore. And then we're going to also pass it some variables. So when you create one of these objects in the class, the first thing it's going to check is this function. And everything you pass to it when you create it will go into this function here. So let's think of some things we want. We're going to want, first off, self. This means that it can look at itself and say, OK, let's say it has a life, which this thing will. We're going to say it has a life. We can say self-life. And we know that that's different than the life of another ball. So if you want something to die after a time, they don't want to die at the same time. So anyway, we've got self. We're going to be creating a ball. So we'll probably want a radius. Next, we'll want a position. This will be the position it starts off in. Then we're going to say color, because we want all our balls to be different colors. That sounds funny, but yes. Next, we'll be the size. And then, like I said, I'm going to fake physics here. So I'm going to create something called max force. And then we're also going to have force. And then we're going to say life, which will be its life span. So we create all these variables. But to be able to call them from other functions within this object, we're going to have to reset them like so. And I don't know if there's a better way of doing this. This is just how I've always done it. We're going to say self-y. So now we're saying, not only is this a temporary variable of y, we're saying this ball, its y, is whatever y is equal to. We're going to say the same thing for x. We're going to say self-size. So we're going to be basically doing this for all our variables up here. Size, we're going to say self.max force. We're not worried about x and y, because it's x and y of where it starts. That's going to constantly be changing. And basically, it's just, well, I'm sorry. Forget what I just said. We do care where it is now, but it is going to be changing later on. I really probably should be creating these in the order that they are up there. But it doesn't really matter as far as the coding. Just once again, I'm very rusty on my pi game here. So sometimes I might mumble a little bit. Anyway, self-force equals whatever force equals at this point, self.radius, should I spell stuff right? Yes, radius self.color equals whatever the color is. Our color is going to be constant, but it could vary later on if we wanted it to constantly be changing color when something else happens. Our object's life equals whatever the life variable is. And then at this point, now that we have all that, we are going to create our circle. We're going to draw it. So we're going to call the pi game module. We're going to say we want to draw something to the screen we created. And what we're going to draw is going to be a circle. And then we're going to say, where are we going to draw it? We're going to draw it to our screen, which is the object we created up here. Next, we're going to ask for the color, which would be whatever our self color is. And once again, at this point, in this actual game, we're not going to be changing the color. I was going to say we don't have to set a self color, but we do, because each time we draw it, we're going to have to call this. So disregard, once again, what I was about to say. Self.x comma self.y. So that's setting the position. So it's drawing a ball of a certain color in a certain place, and now it just needs to know its size. So we're going to say self.radius. Great. So we have our ball object created there. So let's, at this point, come down here below our clock. And we're going to create an array. We're going to create an empty array. We'll call it ball. Once again, let's make this with a lowercase b, because our object class is a capital b, and I think that will cause problems if we make them both the same. And at this point, let's just say ball. So outside, this is outside of our loop right now. So there's our while loop. We're outside of that, because otherwise we'd be creating an array every time it looped. We'd be creating a new array every 60 times a second. So we create an empty array so that we can add to it later on. Let's add one right now. We're going to say append, and we're going to call our function ball. So we have our array, and into that, the first object is going to be one of the ball objects we just defined up above. And we're going to pass it some information. So what we're going to pass it is a radius first, because self is just itself, that you don't need to pass that. So the next thing we're going to pass it is its radius, which we'll say 25. And then we've got x and y. So we'll just choose a position. We know that our screen is, I think, 800 by 4 something, I said. So anything within that range for x and y will go 250 pixels over and 250 pixels down. Next, the color. We want it to have a, well, right now I'll just pass it, did I, yeah, I have the right number of parentheses there so far. Yes, OK. Let's just do 50 comma, 50 comma, 50 for right now. And I'll just give us a mixture. So I think it will probably end up a gray color of some sort, comma. And then we're going to pass it, what's next? We've got color, size. We're going to say L here. I'll get to that in a minute, comma. We're going to say 25 comma, 1 comma, and 100. So life is 100 frames. Force is 1, and max force is 25. Yes, yes, yes. Let's save this, let's run this, and nothing appeared. I got no errors either. Oh, because we're not drawing it to the screen each time. We need to add that to our little loop here. So within our loop, after we fill the screen, because if we did it before we filled the screen, it would erase it right away after we drew it, we're going to say is 4. And then a variable, I'll just say I in ball, colon. And then we're going to say I dot, I might be getting ahead of myself here. Oh, you know what, it is drawing it. I'm sorry, I am completely, like I said, I wasn't planning on doing this tutorial today. We need to create another function, otherwise it's just going to get erased right away. So let's create another function within our ball class here. I hope this tutorial isn't too horrible. Oh, by the way, the viewer that asked for this was OMG Flying Bananas, I think was his name. So this is for you, by the way. So if this tutorial is horrible, I'm blaming you. Oh, and I'm not class, I already have a class. Within our class, define fall. Because we're going to create the circle, and we want it to fall. We're going to pass it itself so it knows who it is. Who am I? Self, OK. If self dot x plus equals self dot force. So here, I actually think I'm doing x and y backwards in my notes, because y should be up and down, and I'm doing x here. So this tutorial is probably going to confuse people more than anything, but I'm going to keep going. Like I said, I wrote this quite a while ago, and I actually am writing this wrong. So if x is less than h minus self dot radius, then self dot x plus equals self force. So whatever x equals, we're going to add force to it, which force in this case is 1. Next, we're going to say, and yes, I'm sorry, I'm just looking at my notes. This should be indented, this should be indented. Then we're going to say here, if self dot force is less than self dot max force colon, we're going to say self dot force plus equals 1. I'll be explaining this in a second. Else, l if, and actually, OK. So l if self dot x is greater than h minus self dot radius or if self dot x equals h minus self dot radius colon, then self dot x equals h minus self dot radius minus 1 self dot force equals self dot force asterisk minus 1. And that's actually times negative 1. Self dot max force equals self dot max force divided by 2. This is me faking physics. And I'll explain it all here in a second. Next, we're going to say, just to save time, it's the same thing. We're going to draw our circle again. So I'm just going to copy and paste that line there. And then we're going to say self dot life minus equals 1. And self, I'm sorry, and if self dot life is less than 0, ball dot remove self. And down here, under our for loop that we started to create just a little bit ago, we are going to say I dot fall. OK, time to explain everything I just did as best I can. OK, so basically, right here, we're creating our array and we're going to create our first object within that array, giving it this information here. Then every time our loop happens, it's going to call the function fall, which is what we just created here. And so every time it loops, it's going to that object, each object that we create is going to check itself. It's going to say, hey, it's my x. And once again, usually x is left and right and y is up and down. I put these in backwards when I wrote this script originally. I guess I just didn't realize it. Anyway, just in this instance, think of x as up and down. If x is less than the height, remember we created that variable up here, which is 480, which is good. That's why we did a variable, because if we wanted to change the size of our screen, we don't have to go and change it in each of these areas here. So we're going to say, if my ball basically is less than the height of the screen, which means it hasn't gone all the way to the bottom of the screen, then take x and add whatever the force is. So let's say x, in this case, I think we made it like 250 as a starting position right here. Yeah, 250. Add one to it. So now it's 251, so it moved down one pixel on the screen. Now it's going to say, if the force, which is in this case 1, it starts off as 1, is less than max force, which is 25, then we're going to add 1 to force. What this is going to do is it's going to cause the ball to accelerate, so each loop, it doesn't move one pixel. It's going to go one pixel the first time. The second time it loops, it's going to go two pixels. The next time it's going to go three more pixels than four more pixels. And it would go on forever, but that's why we set a max force of 25. It's not going to go any faster than that. Think of that as the terminal velocity for this object. Then we're going to say, else if. So if the force is not less than that, we're going to say self x is greater than what this is. I have this invented wrong. I think that's supposed to be within there. My notes are not very clear. Self x, so the height, how far down the ball is if it's greater than the height of the screen minus the radius. The reason we have the minus the radius in both these spots is because if we didn't do that, the ball would go off the bottom of the screen to the top of the ball hit, and then it bounce up. But we want when the bottom of the ball hits the bottom of the screen for it to bounce up. That's why we have the minus self radius. Basically, this is saying when the ball hits the bottom of the screen or it equals the bottom of the screen. So yeah, either way, it's when it hits the bottom of the screen. We don't want, if it goes past, then right away it's going to turn around and go back up. And the reason for that is because we're not going one pixel each time because the force is constantly increasing. Hope that makes sense. Then we have self x will then equal the height minus the radius minus one. So basically it's going to subtract one instead of adding one. Now force, which is going to be changing all the time, we've been adding to it so that the ball moves down the screen. Well, we want it to move up. So force is a positive number now. To get it to move up, we need it to be a negative number. And how do you make a positive number, a negative number of the same value? Not really the same value. But anyway, we multiply it by negative one. So we're taking the force, let's say it's five. We're multiplying it by negative one. Now the force is negative five. And we're going to then make the max force half of what max force is. This will cause the ball to slow down as it moves up. So as it's pulling, it will increase in speed. As soon as it hits the bottom, it starts bouncing up. It will decrease in speed. And in this case, let's say we start the ball 100 pixels up from the bottom. When it bounces, it will come back up around 50. It should be about half, I think, if I'm saying that correctly. Anyway, we're just causing it to slow down here so that it's not continuously bouncing perpetually forever. And then, of course, we're going to redraw it with these values. And then we're going to subtract one from life. We set life to 100. So every time this loops, we're going to subtract one from life. If the life is ever less than zero, then we're going to look at our ball array. And we're going to remove this object. And just this object, not any other ones we might create. And that's one of the many reasons we use objects. So oh, invalid syntax. Ah, I think that's right. Obviously not, because colon. There we go. Run this. Oh, no, I did do x properly. And it is going the wrong way. It's kind of weird that I did that. So let me start this again. Yeah, it's starting off the side of the screen. I apologize. There is a gray ball. Let me get my fingers cracked here. Y is rrr. Trying to show you something. There we go. You might have saw that ball bouncing. It's bouncing left and right. It should be bouncing up and down. Let me go in here and change this. Yeah, I don't know why I wrote it the way that I wrote it. This should be self y. My notes are wrong. Y, y. Because y should be up and down. X should be left and right. Let's try that again. Yes, it's bouncing properly this time, even though it's bouncing off my recording area. You see the ball bouncing there? We'll get to it more. So we created one object now, a gray ball that bounces. You'll see it bounce into the screen a little bit there. And then after 100 loops, it dies. Once again, I apologize if this tutorial is not very good. It was not what I was planning on doing this morning when I woke up. So we're creating one ball here when the game starts. That's a gray color. We're going to do the same thing, but within our loop, when we have mouse clicks. I know this tutorial is getting kind of long, but I do want to keep going. Yeah, I'll just keep going instead of splitting you up into multiple tutorials. Not a whole lot left. We just need to do some mouse clicks here and create new objects when we click the mouse. So we're going to say, LF, so once again, we're in here where when anything time an event happens in the pie game, we're going to check this. We're creating a variable here called event. We're going to say event.type. We're checking what type of event. And if it is mouse button down, then we're going to say z equals one and LF event.type equals, should be all capital mouse button up, then z will equal zero. And once again, I know there's a better way to do this because I think I did a tutorial on it once, but I can't find my notes on it on detecting when the mouse is down. So we're having when the mouse is clicked and when the mouse is let go, but I'm not doing while the mouse is pressed, that's why I'm using the z variable. We created it at the beginning, we're changing it now. Then we're going to say if z equals one, then, and real quick here, let's just copy this line and paste it in here to save some time, make sure that we indent it properly. Okay, so here we're going when it equals one, create a ball there. And then we're going to say, after that, z equals three. This will be a delay, which even if we did mouse down, this would be something I'd want to do anyway, so I guess it doesn't really matter. We're going to say LF z is greater than one colon z minus equals one. Okay, let me explain this now. Okay, so when the mouse is clicked, we are going to set z equal to one. Now, while this is looping, anytime z equals one, we're going to create a new ball. And then we're going to say z equals three. And while z equals three or while z is greater than one, we will subtract one from z. This is using a delay. Otherwise, our script will be creating, if it's running at 60 loops per second, it's going to be creating 60 balls per second. Here, we're slowing it down. We're putting a delay, three is delay. If you want it to be slower, we'll increase that number. Let's go ahead, save this, run it. I'll grab the screen up here. Now when I click, you can see a lot of balls being generated. I mean, just one little click. And I think my fake physics look pretty good. Really looks like some balls bouncing. You know, it's even a quick click. I get one, but if I hold it down for any amount of time, it's like we get a whole bunch. That's where that three comes in. So I'm going to leave that number for right now, but we're going to go back in and change a few other things. First off, the balls are all ugly gray color right now. Let's go ahead and change that. Remember we imported the random module earlier? What we're going to do is we're going to say in here, instead of giving it set values for the colors, the RGB, we're going to say random. Oops, if I wanted to edit mode. Random.random.initure so that we get whole numbers here. And we're going to say a random number from one to 255. Really we could do zero, but then it would be black and if they were all black, anyway. It could be zero to 155. We're going to do 155, doesn't really matter. And we're going to do that actually three times. So just to save time, I'm going to copy that three times. So what that is creating is our RGB. It's going to create random numbers each time. So let's go ahead and save that, run it. And you can already see that one's a funny color. Oh, I put that in the initial one we created and not in our loop. And in fact, we don't even need that first one there, but it's nice to see the game start off that way. But let's just once again, copy that and paste it in here, delete that. Okay, now that it's within our loop, every time I click or hold down the mouse, you can see it generating a new ball of a new color. And once again, you can see how it kind of slows down and bounces a little slower. That's because we're creating that max force and dividing it by two. And the speeding up as it's falling down is because of the force as it's falling down being added to. So here we've got that. Now we've got random colors, but we also here we got the 255, 255. That's where the ball is being positioned. We're actually going to set that to X, Y, which is our mouse position. So if I run that right now, bring it up here. So now, oh, I got X and Y backwards again. I do that sometimes. And really this is just corresponding with what I did when I made the class. There we go. Now we have somewhat of a little particle system with some fake physics that follow my cursor, wherever they go. Each ball has a life of 100. So after 100 loops, they die off. That's why they're dying off in the order they're created. What else do I want to do here? I think that's a majority of, that's pretty much it. I mean, we could do different size balls. We can give them different life, but let's quickly review. Once again, we have a three delay on this. If I wanted to create balls a little slower, let's have a look at that. And once again, I know this is a long tutorial. I usually try not to make tutorials this long. Let's go 10 and see how that goes. You can see it's creating balls a lot slower now. So let's put that back to three. I think three is a good number. It gives you more of a particle feel to it. Let's review the whole script here. Importing our modules, pi, gang, sys and random, so you're gonna create some random numbers, setting variables for our height and width, setting z, which is our value of when the mouse is being held down and our delay for our ball objects, creating our screen and giving it the height and width that we set up here. Then we're gonna update our pi game display. Next, we have our class, which is our object. In this case, we're calling it ball, and we're passing it some information. Radius, x, y, color, size, the force, max, force, life, and then we're setting itself to that. See, without creating a class like this, we would have to rewrite code for each ball that we wanna generate. But creating a class and creating an array and then adding or appending that class to that object allows us to create as many as we want. So you're gonna have a game with a bunch of bad guys, a bunch of bullets, let's say you're playing Doom, a bunch of barrels, you don't wanna have to write code for each one of those barrels, even if you're copying and pasting your code, we get huge. You're gonna create classes, you're gonna create objects, an object-oriented. Okay, we got our clock here object, which later on we're setting to 60, which will limit the speed of the game. Then we're constantly grabbing the position of the mouse. We're checking if we exit out of the game, and then we're gonna say if the mouse button down, you know, set this Z value to this, if the mouse button is up, set to this. Every time we loop, it's gonna check a Z, if it's one, create a new ball, create a new object, and then reset Z to three, that will give us our delay because then if Z is greater than one, we're gonna constantly be subtracting one from Z. So whatever Z is, we're gonna subtract one from it, so it's three, then it's two, then it's one, and then it creates another ball. Then after we create it, we're going to fill the screen with black, which could actually be put above this, actually the way we're doing this, the first frame of the ball is being erased. So technically, even though it's, theoretically, one-sixtieth of a second that we're missing there, I think this should be up there. So we're clearing the screen, creating the ball, drawing the ball, oh, but now, no, that's, you know, put it back down there, otherwise we'll have two balls drawn on the screen at the same time, one pixel apart. So we're just going to ignore the first drawing of it. So then here, we're gonna say, every time it loops, we're going to check the array of ball. There could be 100 objects within that array. It's going to check every single one of them every time we loop, and it's going to run its own function, each object's function of fall, which is up here. And so every time it loops, every time it loops, one of those objects, every object, is gonna check itself. It's gonna check its Y, it's gonna check the height of the screen, it's gonna check its radius, it's going to check its force, it's going to add to its force, it's gonna add to its max force, or divide from its max force, it's gonna run this function every time it loops, but for each object individually. And then we're gonna update the screen, which draws it all to the screen. And that, of course, brings us to our little particle game here. Once again, kind of a long tutorial. I do apologize. I hate YouTube videos that are longer than 15 minutes. I have no clue how long I've been going for, but I'm quite sure I've been going for more than 15 minutes. So there's this. Check out the link in the description. I will post the code to this on Pastebin or on my website somewhere. There should be a link in the description. Thank you for watching. Please visit my website, filmsbychris.com. That's Chris the K. Link to that in the description as well. And I hope that you have a great day.