 All right, good afternoon. Welcome. Happy Friday. Hope everybody is having a good day. Let's see here. Let's say hi to some folks in the chat. Let me find my chat windows here. Let's put this one up. Good evening. Good day. How's it going to see Grover as well as Jeff and DJ Devon? Let's get some Pac-Man GIF action going here to get us ready. We're shadowing for anybody who hasn't seen the title right there. Let's see. Where is this one? Let me put it like that. I got it really buried down there. There it is. Okay. Sunny at 65 in LA, which sounds pretty good to me. It's sunny here. I actually managed to get some sun for the last couple of days, but we're near 65. I'm not sure. I don't think we have made it back above freezing all the way yet, but we're much warmer than we were last week. So I'm feeling pretty good about it. All right. Let's see here. Can find this one. That is not what I meant to do. Let's see here. I've messed up my OBS somehow. One second. We're going to switch this. Okay. Here we go. How I got this browser stuck without being able to be seen, but we give it the old flip-flop around and we're good to go. All right. Work on the font issue. It was a font. Last week, we were looking at something in display IO, which was, or I should say with display text, which I do think was in the font. We're not. That's not what I'm working on tonight though. Tonight we're on a new thing that I'll tell you about in just a moment, but let me start off with the kind of top level intro for anybody that might be new. Welcome. First of all, thanks for tuning in. My name is Tim. I go by FOMI guy on GitHub and Discord. This is the deep dive, which is a weekly live stream program. It was started by Scott Shawcroft, the lead developer of circuit python. When Scott's not available to do it, then I will be here streaming if I'm available. Circuit Python, if you are new and you don't know what that is, you can learn more at circuitpython.org. This is basically a version of Python that can run on tiny computers called microcontrollers. There's a bunch of pictures of them over here. Basically, these little devices will plug into your computer with a USB port, most typically. Then it shows up like a thumb drive and you can edit the code file that's on that thumb drive and save it, and the computer that's actually inside this little chip on this microcontroller will read and interpret your Python code and execute it for you. It can interact with other peripherals. Oftentimes, these microcontrollers come with lots of these little IO pins along the edges, where you can connect up other stuff. There are all sorts of different shapes and sizes of these devices with all sorts of different capabilities built in. Some have alligator clip little holes there. Some have the pins that let you plug them to a breadboard and built-in battery charging. Some have displays with touch screens. Some have buttons and knobs and screens and all kinds of different things going on with these microcontrollers. The 469 of them that are listed on this page, though, all can run Circuit Python, which is kind of the primary focus of this stream. Sometimes when Scott's here, it tends to be working in the Circuit Python core, kind of on cutting edge stuff. When I am here, I don't go quite as deep. We raise the level a little bit and typically I'll be either in library land or today we'll be working on a project where we're just using Circuit Python code in the project, not working on the infrastructure or the libraries or anything like that. But if you do catch it when Scott's here, it tends to be way, way, way more into the core working on the cutting edge stuff, brand new stuff. So tune in if you are interested in that sort of stuff. Thanks to everybody for watching along. Adafruit, this is their website, Adafruit.com. Adafruit is the company that allows Circuit Python to exist by paying the folks who work on it. Scott, who does this stream, is paid by Adafruit full-time. Like I mentioned before, he's the lead developer of Circuit Python. There's a couple other folks who are paid by them full-time to work on Circuit Python. And there are other folks like me who are paid part-time to work on the project. So a huge thank you, as always, to Adafruit. And if you want to help support Circuit Python, kind of the easiest way that you can do that is just head over to Adafruit.com and buy yourself some hardware from them. They have all sorts of the different microcontrollers themselves, as well as different things that you can plug in to your microcontroller in order to interact with. And today's thing for me is, I'll get the camera pulled up here, is one of the Qualia S3 driver with one of the relatively oddly shaped displays. Let's say five is this one. Right, and let's focus it in. All right, so I've got the Qualia driver board. My camera is not quite wide enough to keep everything all the way in focus, and will want to be, you know, the screen is kind of the business end of this project, so we'll be mostly focused on the screen. But I do also have this big red push button plugged in, and that is just connected to the little JST-A0 plug right there. So I've got alligator clips, which then clip into the leads on the bottom of this. So nothing too complex going on for the hardware just yet, and then software-wise as you could see from the title or the foreshadowing from the GIF a little bit earlier, the project that I'm working on is a kind of like a Pac-Man clone. So it's a Pac-Man where instead of you having a big map where you can run all around everywhere, you just have this one sort of hallway. You can run back and forth. Basically you press a button in order to turn Pac-Man around, and that's it. So you can chomp the little pellets, and the part where we're going to start working right now is adding the ghost, because what you can see right now is Pac-Man's running around. He's chomping pellets. I do have the big pellets, which are candies or whatever. I don't know what they're supposed to be, but they make it so you can eat the ghosts, but I don't have any actual ghosts yet. So that is where we are starting right now is adding some ghosts. Tiny bit fuzzy. It is. If we turn the light, it'll be better. And yeah, it's quite a bit fuzzy, but it's mostly because it can't focus without enough light. But then it becomes a game of the glare. I can sometimes win, and I can sometimes not win, but we'll see how we do. That's the wrong way. And then we have a little bit of a legitimately awkward thing in that it can't really focus on the entire width of the screen, because the way my camera is, it's at an angle. So we kind of get like a weird little shift where you're really eyeballing it super close. What you'll see is half of my screen is in worse focus than the other half, but that is just how we got to do it, because that's how my camera is. Okay. There is code pie. Wait, wait, wait, we're not going down far enough. It's not even open. All right. So this project right now is built in a couple of parts. There's the code pie, which is the main script, but it really doesn't do that much in code pie. It instantiates a game object, chopper game right here, which is a group. So it extends group so we can put it on the display. And then it also just manages everything inside the game, which includes the player, you know what I kind of call loosely the map. So the tile grid that represents everything in the world. This all gets managed inside the game. And then what we're going to do is add the ghost right now. And in order to do that, we're going to make sure do I have a ghost entity? I think I do. Okay. So let's take a quick look at this. So ghost extends entity entity is basically like a tile grid that also has a direction. Oh, and it can cycle through multiple sprites. So it's like an animated tile grid that has a direction. That's pretty much all an entity is. It's a helper class that represents that. It allows you to set the direction to up, down, left, right. It doesn't handle movement by itself. So some other part of the code still needs to actually move it in the direction it's facing. But this code allows it to have a direction. And then it also has collision, which is nice. So we'll be doing collision check on the ghosts and the player in a little bit. The first thing though is that this, a lot of these classes actually came from an older project, which had a larger map. And it was not only did it have a larger map, the map was being parsed out from a tiled, a tiled map from the tiled map editor, which is sounds very non-specific, but is actually specific because it's the name of the map editor, tiled map editor program. This program is like an open source map editor. And our map was in the version of Pac-Man that I made before was built inside of that editor. And so the thing is that these classes probably still have some references to some stuff that's in the tiled map, which we're not using. We're going to cut anything out that's there. We have a direction. We hold a tile grid, which gets a pallet. We have a width and a height and a tile width and a tile height and a default tile. So we just set all that stuff up sprite. So this gets called in order to cycle through the different sprites in the animation. That looks all good. Setter for the direction, getter. So these are just, we won't need all of these. I might not even really need any of these, but I'm going to leave them for now. Yeah, this goes along with them. I don't know if we'll end up needing it. X and Y. Setters, tile grid is colliding. It's actually not other entity. Okay, there's actually not anything referencing the map, which I'm kind of surprised on the player, which there is also a player helper object. There was some more stuff referencing the map. Although this is entity, we need to be looking at ghost a little bit as well, actually. Okay, so it is, every time GameTick is called, it says if the direction is not none, get the pixel location of the next place where we would step to, basically. Next tile chords. So that's getting the X and Y coordinates in the map plane system, in the map coordinates. Is tile movable? We don't really have that concept anymore. At this point, every tile is movable, so I think we just get rid of that. And we actually also don't really have the concept of up and down directions, so I'm just going to get rid of both of those as well. And then we don't need any of the else. We will eventually need logic that teleports us to the other side when you run off the edge. It might move this out of ghost and into game. At that point, ghost really doesn't have much need to exist, I suppose. So inside GameTick, so this gets called really, really fast from CodePy. It calls GameTick on player, which will ultimately be responsible for the animations, which in Pac-Man, the player animation is the chomping of the mouth. And then it handles the movement right here, including the logic to teleport to the other edge for the player. And then it also handles the logic right here for eating pellets. And then it would call GameTick for every entity. I mean, it does call GameTick for every entity, but right now we don't actually have any entities. An empty list. I think we'll make it so that we have a ghost. And then we need to create our ghost. Let's say... So a lot of these can stay the default, but bitmap and pixel shader do need to be filled in, but they will be the ones we already have, although they are a little bit lower. So I'm actually going to come down here. I'm actually going to come further. We're going to do this after like... Wait, are we doing this twice? Yeah, I don't... I don't think that's just happening twice. I don't think we need to do that. Let's make sure that doesn't break anything. JP... I had the same screen at DriverBoard. I think it was JP Project of the Week. So the one that I've got is... I didn't catch a project of the week, or a product of the week, I should say. This one that I've got is the 240x960, and there are a couple of other ones that are not the exact same, but are similarly kind of oblong. I think there's like an 840x360, or... I don't remember them all now. How's it going, Orbital Cat over on YouTube? Thanks for tuning in, my friends. Hope you're having a nice day. Let's stop that one. Okay, so we did not break anything. I am gonna put the ghost down here, I think just after the player, right? We might as well. So let's say ghost... Actually, I think I have it pasted, right? Or copied ghost as ghost. That's gonna be the sprite sheet, and then that's gonna be the pixel shader, which is self.pallet. And then we can say self.entities.append the ghost. And then we can also append the ghost's tile grid to self, which is the group that represents the game. That way the ghost will actually get drawn. So we should get a ghost, but it will probably be just somewhere not moving... Well, it might move, actually. I don't know. Maybe it will move. I don't know. Probably it will crash. It will just likely it will crash. It didn't. Okay, we got a ghost, but the problem with our ghost is two problems. One of them is it's outside the map, it's outside the walls, and then the other problem is it looks like Pac-Man, not a ghost. So let's fix that. I think for that, we can just use the default tile here. There's a default tile, and that is the number, the index of the tile, which is... We want ghost 1, 2, 3. So that's... It's actually zero, so this is 4, 9, 10, 11, 12 is the ghost. We could say default tile 12, and then that's already set up to draw it for us, I believe. It will be still outside. Yep, there we go. Okay, so we've got a ghost. It is still outside, so let's work on that. That's probably just as easy as this, basically. Same thing we did on the player there. We want to set that Y value. It's not a self, though. We're just going to say ghost. I think there was an X and a Y in the... in the constructor, it wasn't there, but that's fine. 12. This feels pretty random. This feels pretty magic numbery. I don't exactly remember why I came up with 12 there. 15 makes sense, because the sprites are 15 by 15. 12. I don't know why it would be 12 plus 15. Maybe 12 is how far off the top edge we are? I don't know, because there's also scale involved. It was a Qualia S3 RGB666. Yep, all of that is definitely the same display board that JP was talking about a month ago. I got you. Okay, so it wasn't this week's one. But yeah, that's exactly what this is. Qualia RGB666. I have the driver as well. RGB666 40-pin TFT driver. Okay, so we've got a ghost. He's actually inside of our hallway. He's not moving, and he's also not hurting Pac-Man. It should be that when you run into the ghost, then you lose a life. In our game, I think we're just doing it to where you lose the game. So... let's do that first, even before we worry about movement. And where should we do that? I think let's just do that inside of... so I mean, technically, I guess we are... we should be calling gametick on... and then, yeah, just direction was never set. So actually, if we would just set direction, then the ghost would probably move. But our next problem would be is there's no logic that is teleporting it from one edge to the other, which means it will just keep running off the edge forever and ever. It's not very useful. So let's do the collision first. So let's just say if. So inside gametick, we're going to say if self.... is it colliding? Yeah, it's colliding. And then we're going to say the game object, which got passed to us right here in gametick. The game object. And there is a player entity right there. So we can say basically for this ghost, we're going to be looping over all the ghosts calling gametick. So when we are on this one, we're going to say is this ghost colliding with the player? If it is, then player loses. We're just going to say like... we're just going to say like game object.gameover equals true, which I don't think even exists yet, but I'm about to go add it. There is a game over, actually. Oh, oh, no, that's the one I just added. Okay. Thoughts on templeOS. I do not know anything about templeOS. So I basically don't have any thoughts on it. Is it like a typo Linux or is it a mobile thing? Yeah, I don't know anything about it. Game over, we're going to set that to false right here. And then inside a gametick, we're going to make a top level check whether the game over is the game over or not. So we're going to say if not game self.gameover, then we do the tick, which will make everything happen. So basically when game over is true, it will make it so that game tick is pretty much not doing anything, right? Nobody will be moving. No pellets are getting eaten. No ghosts are hurting us anymore. I mean, we already lost to a ghost, but... do that. So now when we run into it, we should get a game over. And then that should stop everything from moving. So let's check if that works. And our ghost doesn't move yet, but it should just stay on the edge there. And we should be able to run into it to test. Oh, actually we don't even have to wait because we also spawn at the same spot and we already know that that's working because nobody's moving. Perfect. Let's also do like a print game over just so we have like some more one-time sort of like output for debugging and stuff. Print game over. We'll probably also do something where it like shows the score maybe and it sets it to where if they press the button then it will start the next game again or something. But now we'll just do it like this. This is a display I got for. Single RGB TTL, TFT 4.5, 320 by 960, no touchscreen. That one is close, but it's actually a little bit taller in the small orientation. Mine is 240 by 960. So yours has got an extra, what is that, 80. Extra 80 pixels on the small side. You've got a little bit of extra room still. Open source OS with 3D sprite creation baked into the console. Interesting. 3D sprite creation. So this is like basically aimed at like game developers as I'm assuming the gist of that. Temple OS. 64-bit non-preemptive. Oh wow. I like the kind of like ASCII Huey. Oh, this might not be the same thing. This is not the same thing. Yeah. This is like temple like churches. That's a different thing. Maybe let's say temple OS 3D sprites. The space makes all the difference there. It looks like 3D model viewer. Interesting. I'll have to check it out. But yeah, no experience with it yet. Alright, so we did get our game over there. That's good. Let's get our ghost moving. But let's also try to build the boundaries while we're here. So we have the game object. How are we doing the bounds in GameTick for the player? So we're saying... Where's GameTick? GameTick, here we go. So let me do this. Also get rid of the yellow lines. The bounds. Okay, so we're just saying player entity zero. So we have self.display size. So on the game object, there is display size. Okay, so yeah, let's go back to our ghost. Okay. So if we're moving right... Ifself... Actually, I'm going to copy it. You know what? We're going to copy paste action this. I'm going to go big. Then split. I'll go back to where we just were. Okay. We're going to go back to where we just were. GameTick down here. His player is an entity and ghost is also an entity. Therefore, the logic is basically the same, honestly. We can say... We just do self.x and width. And we just get rid of all of these. Oh, not that one. That will be GameObject.displaySize right there. Get rid of that. GameObject. There's another one of those we get rid of. Okay. And... Yeah, movement is still somewhere else. Actually, movement must be in here. I see. So I'll probably move it to where the logic that teleports the player to the other side is here inside a player game tick. Whereas right now it's actually in the GameObject game tick. But I'll probably move it to keep it consistent. I think I like that in the helper object better. So it basically moves the player and then it does the check. So we'll keep that the same. While we're inside... Yeah, we'll keep that the same. So let's go do those first. And then these. We already have colliding. Okay, so now as long as we just put a direction on our ghost it should start moving. So we should not spawn it in the same tile as the player though because that means we'll instantly get hit by it. Let's move it some... So I think this is like pre-scaled. So we're gonna say like move it 15 pixels but that's actually gonna move it 15 pixels times 3 as a scale on the group. And what I want is for it to be like three-quarters of the way across. So let's say display size 0, integer division over 4 and then multiply that by 3. That should get us pretty much right to three-quarters. It might not be exact exact depending on your screen size and integer division. Be pretty close. Alright, let's close that one. Let's put that back. Let's get to here, restart, run it. Ghost is gone. Okay, so I think though because I did the 15s wrong we need it to be way less than this actually. We need it to be like... We need it to be like basically over 3 on top of this because we're multiplying by 3 and I guess we could just not do that. This is a little bit awkward to multiply by 3 and then divide by 3 but I actually kind of like it better than just getting rid of the multiply by 3 because if we just get rid of this when I'm reading this code in my mind this is telling me that we're gonna spawn at one-quarter of the way across the display like right about here-ish but because we actually have a scale of 3 going on we're trying to get to over here and even though this is a weird way to write it it's what we need or it's one of the ways to write it that makes it more clear in my head at least what it's doing so that's the way we're gonna do it so our ghost is over there now let's give it a direction we should be able to just say ghost.direction and let's start it going left that way it's moving towards the Pac-Man at the beginning it goes a little crazy when you save I've noticed and we need to click pretty fast because we're gonna be going towards the ghost alright nice okay and now one thing is we move at the same speed as the ghost right now which makes for the game to be not very hard and not very fun so we actually want to move a little bit we want the ghost to move a little bit faster than Pac-Man that way it can actually chase you down and that way you actually have to use the button to turn back and forth to get away from it and importantly to get to the the big pellets that let you eat the ghost let's make it so when the game is over if you press a button it will start a new game so what we're gonna do is we're already checking the button right here we're gonna say is it printing pressed even though the game is over? yeah okay so let's just let's just add a thing in here that says if game object game over we're gonna say not first so if it's not game over this is the normal stuff to do and then else if it is game over and you press a button then we wanna restart basically we wanna begin again so we're gonna effectively we're gonna say game over zero maybe we should have like a game object restart actually I think we should have that or maybe it should just be game object start and we should call it the first time too I don't know let's do just a restart for now basically we wanna move the player back to the left we wanna move the ghost back to the right and we wanna reset the direction to be the default ones and we want to respawn all the pellets I don't know if I was just on zero or I don't exactly remember where we started the player entity so ghost is actually on entities which is a list technically it's entity zero if we ever add multiple ghosts we're gonna have to deal with that for right now I'm just gonna hard code it to the one player to the left ghost to the right and then respawn pellets I think we can just say spawn pellets although yeah okay so if there were any pellets that weren't eaten yet they would just get turned into pellets again just fine that should be no problem previously the only time we were calling spawn pellets there were no pellets spawned so I wasn't sure if that was gonna cause trouble like it would double them up or something but that's not the case it's just gonna overwrite the ones that still exist if there are any so now when we're in game over mode if we press a button we call restart restart should get us to where we can play again I think so I'm gonna run into it on purpose okay game over I'm gonna press a button okay one thing it needs to do still is it needs to set game over to false otherwise it stays paused so pretty close set game over to false game over press alright back in it nice got a double press there didn't even mean to looking good let's make it so the ghost goes a little bit faster let me also pull up the let me also pull up the actual game here real quick this is the inspiration this is where this inspiration came from and in this one what you can see is the ghost is faster got me from right side that time so if I get myself going in the same direction as the ghost it will gain on me and eventually it will catch up to me feel like that it's gaining so you pretty much have to use the pellets in order to live so does ghost have a doesn't have like a movement delay actually player doesn't have a movement delay either oh it does no no no that's animation how fast are we calling game tick right now okay here's I see what I did okay so we put the delay we have a tick delay actually really low though alright we're gonna swap this around a little bit right now we have this tick delay here which is like our outermost logic in code pie we want this to be deeper though I want it to be the player is gonna keep track of its own movement delay and the ghost is gonna keep track of its own movement delay and they'll each have different movement delays that way the ghost can move slightly faster than the player and then on the code pie in the loop here we're just gonna call game tick as fast as we can the player will slow its own movement down with its internal check the ghost will do the same yeah and then it should be fine that that's getting called faster and then it will actually make it so that our button can even be a little bit more responsive because right now we actually have this kind of effectively like an extra delay in our button so we're gonna say move delay step delay let's call it move delay and I'm just kind of guessing I mean right now it's hard to say it said that 005 but that seems really fast I think once we split this up we don't need it to be quite so small say start like that and then see where we end up we need a alright so if the current time is greater than the last time we moved plus the delay then it's time to move again we do all of this I think I'm ready to delete this stuff this was logic about is tile movable which we don't have the concept of anymore and by anymore what I mean is like this version of the game doesn't have that concept but some of these classes I took from an older version of the game that I worked on so the player should slow itself down now let's get rid of this logic and let's just call game tick as fast as fast as we can just gonna call player tick which now has this delay in it and then we're also gonna do a similar one inside of ghost so 005 I did on the player I'll start with 004 another thing we might do eventually is maybe the ghosts as you get further in the game maybe this will go down more so the ghosts speed up over time like once you get to higher levels basically don't really have levels per se but same thing here if the direction is not none if we need our we actually need init def init actually need to make sure we put the right stuff here otherwise it won't work well stuff practical even though it's game yeah I think it's nice like games honestly you can games kind of push push software in weird weird ways if you want to make games you oftentimes and end up finding some like neat kind of code modules that you end up making for a game but ultimately will apply elsewhere alright so we want to call super which we can basically gank from here and then we only have one so I don't need to bother with the now variable I'm just gonna say last move time you know arguably we could even put this on the entity class we wanted to but now I'll leave it here last move time time dot monotonic then inside of here we go if so here I will get a now now equals oh you know what we needed to do inside player which I didn't which is to update the last move time we checked it but we never updated it so I'm gonna hopefully not forget to do that but I'm gonna keep going here first now equals time dot monotonic if now is greater than self dot last move time plus ghost dot move delay what you serious come on now there we go okay then we do all of this stuff and we could put the collision check in there as well currently it's actually outside of that which I guess is why we were colliding even before we started moving I think we were at least here we go now we say only when the move delay is passed that's when we do take our step and we only need to check for teleporting from the edges like when we take a step because obviously if we didn't move then we didn't hit an edge we'll need to check collision actually we should check collision whether or not the ghost moved yeah because it could be like a frame where the ghost doesn't move but the player does and in that case we still it should still count as losing in fact I'm almost I think we just move this out of the entity right this should just be in the game game tick I think so tick the player teleport if it needs to eat the pellet if it needs to tick the enemies which are entities and then after that's done do the collision check so if and we're inside the game so it's no longer self instead we're going to say we could do it either way but I'm just going to say self dot player entity is colliding and then technically we only have the one we only have the one ghost so we can just hard code it to zero but if we add multiple then we could loop if the player is colliding with the ghost then we get the game over we set the self game over yeah and now because game game tick is getting called faster now it means even if one player did not take a step that frame you can still get to game over is perfect I think that's exactly how we want it especially if they're going to move at different rates my guesses are we're going to have to tweak the speed probably somehow we went back to the ghost being pacman that's okay well there's oh okay but this is the wrong file okay that's where our 12 came from we had 12 plus 15 that's because we're setting the map to 12 and then 15 is each tile so that gets us to the middle our map is technically three tiles tall but the top tile and the bottom tile are both walls I live in my own code does something and I'm like what why did that happen agreed I think that's honestly one of the funnest parts of programming for me is like just seeing what goes wrong like there's never a dull day of what goes wrong in programming yeah no I don't know that makes no sense to me I guess it's because we are so we're calling entity game tick we're calling entity game tick are we calling super we're not calling we probably should call super in here is entity no okay never mind entity doesn't have a game tick why would it be changing okay we need args here which we do the stars are on the other side here I think right oh wait no it doesn't have them here I think we need those because we want to pass these through in particular default tile which for the player happens to be correct but for the ghost is not that's not how you do that do it at the front there and convert string to int so it's tough because line 27 is actually a bunch of lines any of those should be ints though or strings are other I don't get why it changed like we had the ghost correct a second ago does this like not preserve type or something does it need the double star here as well I was thinking that was only one star but maybe I'm wrong okay I don't do a lot of of poly I don't think my button press is working oh it is wow you just have to hit it really fast I'm not sure the ghost it's not gaining on me though oh I never actually updated my last move time still I said I needed to remember to do that and I didn't so it's actually both things are moving at the speed of the game ticks right now alright so here we need to say let's move time equals now we need to do the same thing for player how's it going Axel Magnus hope you're having a nice day my friend let's put that one back there oh okay ghost is now faster than the player but both are very slow I am surprised actually how slow that is because we did .05 which is 20 times a second which is pretty fast it's a big display is auto refresh on wondering if we're refreshing the display sometimes when it's not changing so what if we would let's see we're not passing the display we're gonna say moved equals player moved I don't know if this is actually gonna make a difference if it doesn't I'll probably cut it back out we're gonna return true oops I accidentally deleted the end if we actually moved we're gonna return true and then if we didn't we're gonna return false anything on the ghost and in here we're gonna check if either one of them moved technically I'm gonna take this loop off for right now pass the display here so basically we only refresh the display if something moved and see if that makes it so it seems like our stuff moves faster and if it does then basically that will tell us that we were spending time refreshing the display sometimes when stuff wasn't moving but now honestly that looks like the exact same speed to me so I don't think it made a difference I guess though the other thing is we're only moving by one pixel at a time which is not very much and it is a 960 pixels wide screen so it's gonna make it seem like we're moving pretty slow alright we have got to run to the restroom so I will be right back as I take another drink of coffee which is not helping but BRB alright so I want them to go faster there's a few ways we could achieve that we could make them take more than one pixel step at a time um that will start to make them look like they're kind of chunky though if we go too far with it the other way we can make them faster is just lower the move delay on the player kicking it back down to where it was and on the ghost let's keep it with being slightly less and see what that feels like it's actually still feels slower than before also not sure that it's catching up we're creating a dictionary for no reason in fact we're doing a bunch of math in here for no reason also let's get rid of the return true return false let's just go back to auto refresh I think that didn't really seem to make a difference it just adds extra logic the print statements they probably are some yeah no they probably are they slow it down a little bit yeah we should turn them off some of them or most of them really I wouldn't expect it to be to the degree that we're seeing but it could be possible I do have a lot of prints in there actually it should be auto refresh true by default I think make sure it's even working or not okay we're actually a lot faster now the ghost is probably too fast but I put it down to .0002 we're printing less we could still print even less it looks like we're still creating a dictionary maybe we could get rid of that potentially although map tile changed that's only whenever we eat a pellet so realistically it's not getting called that much there's only 21 pellets .0002 is what I'm looking for on our ghost so let's get back into here let's make our ghost not so fast so if I put the ghost back to .0004 will we get to the point where it's actually going to gain because a second ago they were it was like it wasn't really gaining the ghost gaining on me I don't think it is alright let's get rid of the other print I wonder if this would be faster if it weren't a dictionary it's nice because it's a dictionary it makes it labeled but it's technically just two ints it could just be two ints or it could be a tuple of two values we have way less prints now I'd say the speed of pac-man feels pretty good the ghost I don't think is gaining on me um so I'm not actually convinced that our time difference that we're using right now is big enough to matter one thing we could do okay hold on what if I just comment this out do we run faster so basically is this part of the code using up enough CPU that it's slowing us down no not really that seems like the same to me so there's no gain to be had by trying to make this smarter really if we go to 6 here does it make it enough to where the ghost actually gains yeah the ghost is way the ghost feels way faster now I will say though it feels more like the online game because I do think in the online game the ghost feels very punishing feels like way faster than you basically you have to really be good about getting your pellets to be able to eat it whereas it's actually if you start pretty far away from it it takes like three or four trips all the way across the screen here it's not too bad I feel like that's a good starting point and then eventually we could get things speeding up like maybe eventually we get the ghost to moving a little bit faster maybe we also make the pacman move a little bit faster so that you kind of have to make your decision quicker and then once you get to like super late game levels potentially we could have them both start taking two step pixel like two pixel steps instead of one pixel step which will basically make it seem like double the speed instantly so we have a lot of room I think to speed up still so let's try to get our eating of the big pellet to turn the ghost to be edible and we kind of need the visual indicator for the ghost being edible in order to be able to like see if it's working very well so I think I'm going to do that real quick the ghost turn blue whenever they're edible and they actually blink between blue and white whenever they're about to turn back to regular so I actually want two new tiles technically we could get rid of these two pacmans and these two pacmans I'm not going to mess with it for right now though we have empty spaces still so we might as well just fill them up there we go what color should we make the eyes when the ghost is blue it's kind of random to not have these next to each other right maybe we should move them next to each other you know it would be kind of random to have a gap here about whatever it's fine I think red eyes on the blue ghost coming up I like it thank you for the idea let's go well we can't do paint bucket because oh yeah I can actually well I can't because I'm not on this one there we go okay but then it does make it so I can't paint bucket the eyes but that's fine let's do could I have done it first no because it's already red okay then we'll just pencil the eyes like that and then this one we want we need a white one but we have should we change the eyes to black the whites of its eyes I think we probably should right next did I just put a okay so when the ghost is edible it turns blue and then a little bit right before it's about to become not edible anymore then it blinks between blue and white and then whenever it's done being edible it turns back to red so let's save this which is I'm actually gonna save the gimp file which I don't think I did before should have because I want to keep the source file for this just in case but then obviously that's not what we're gonna put to the device for that we're gonna export a BMP which we can then copy to the device okay let's do some things alright so ghost it's like an entity because entity has sprites which can be a list of sprites the indexes within the sprite sheet that that entity can cycle through whenever you call next sprite so for the ghost we don't actually need to be calling next sprite any time except for when we want it to blink when we want it to stay red we don't need to it's not animating so we don't need to cycle when we want it to stay blue it's also not animating so we don't need it to cycle but then when we want it to blink black and white that's when we're gonna use our next sprite mechanism I believe so let's do that after let's just work on turning it blue first so what I'm gonna do is on ghost I'm actually gonna make a self.edible which will be false by default and then we'll set it to whenever the player eats one of the special pellets and then when it's true it will have different logic for the collision which we'll worry about in a minute but it will also change to it's blue costume but we can we actually wanna do that where we wanna do that when the player collides with the big pellets so that's already actually happening down here so right now we're actually checking both types we don't wanna do that so 4 is the small pellet and 9 is the big pellet I'm actually gonna change this to 4 and then LF current tile type is 9 we'll have separate logic now so some of it's the same change map tile that's gonna be the same score that could be the same pellets minus 1 that can be the same yeah maybe we should have done maybe I should do it this way actually that way we're not copying as much code so current tile type if it's 9 we want to go and set the ghost to be edible which we are in the game object we don't need this anymore let me delete that they're in the game object so we're gonna say self.entities0 cause we know that's our only ghost we're gonna say.edible equals true and then we're gonna go self.entities0 and we are gonna say tile grid 0 0 equals so that's 4 9 14 15 16 17 18 is blue current tile type if the current tile type is 9 so if we ate a big pellet turn the ghost to be edible true and set its tile to 18 so right now it has no logic to turn back but let's see if even that part is working or not I'm always a big proponent of kind of take the smallest steps you can although sometimes I don't follow my own advice and I do a bunch of stuff unpositional ok here's where this helps us out cause if I had changed even more then we'd have to trace back through all this so 98 is what right we got rid of display good call thank you nice alright let's work on the collision so when the ghost is edible then if we run into it it's not game over instead it's like you get some points or something I assume dude that is where so that is in game tick we had entity is colliding so that's now gonna say if self.entities 0.edible so if it's not edible then we get the game over else ghost ghost is edible nom nom nom then we do what I think we should so we want to spawn a new ghost we do it instantly or do we wait it waits it turns around so what it does is actually the eyes it's like the eyes fly off the screen and then once the eyes get to the edge that's when it respawns so it actually does give you a little bit of time before it respawns hmm I think what I'm gonna do is I'm gonna say self. that entities 0.re spawn time equals and then I'm gonna say now plus ghost.respawn delay we don't have a now yet now equals time not monotonic then we'll make a ghost.respawn delay let's call it wait for a second excuse me so basically that will be none most of the time we'll say if not respawn time self respawn time is gonna serve as a when it's none our ghost is currently spawned and when it's a number we are waiting until that number to respawn it we're not making a new one but we'll choose a location for it and set it and make it start moving again and stuff what's wrong with now oh okay it's uh respawn time is none we do everything just like we were before else if it's not though then it's a time so then what we do do we have a now in here we do let's take this out so we know that we are waiting to respawn so we're gonna say if now is greater than self.respawn time then we want to spawn which means we are inside the ghost we do have the game object though so we know where the player is at I want to spawn like far enough away from the player that they don't just lose although does it do that I don't know it comes from the side just spawns from the side so yeah let's just pick a side at random I guess is that yeah those are inclusive I think so self.x is zero and also what am I doing okay let's do that actually that's a little bit more like actual code so if we're gonna spawn left we set it to zero we set the direction to right and then else if we spawned on the right self.x equals it's gonna be it's kind of magic number but it's gonna be 21 times 15 we could look that up off of the game object or something be slightly less magic number but I'm gonna leave it like that for now that's gonna put it over there we do want to set it to be the direction left direction entity direction and then if we spawn we also need to go ahead and set respawn time back to none otherwise it'll try to respawn again which will not be good alright so when respawn time is a number we don't we effectively don't take any steps the ghost doesn't move and we just wait until the number has passed the current time or vice versa we reset that so that it will flip us back to the rest of our logic we basically flip a coin to decide if we're gonna spawn on the left side of the right side if we spawn on the left side we set x to 0 we set direction to right we spawn on the left side we set x to the far right and we set direction to the left it sounds good to me so as long as we're setting respawn time when we eat the ghost to the current time plus the delay I think that should work yeah I think that should work the ghost it's not gonna it's eyes are not gonna fly off the edge in fact I think it's probably just gonna stay wherever it's at but like logically I think it should work maybe yeah yeah yeah it stayed oops okay yeah and then it's never turning back to red so I can just eat it over and over again alright let's make it let's make it disappear when you eat it and maybe later eventually we might do the eyeballs flying off the edge but for right now let's just make it disappear and so for that what we're gonna do is we chomp it and then what we're just gonna say self entities 0 tile grid 00 we'll reset it to be not in edible mode as well so we'll set this to we could do any of the empty tiles technically or we could do the back tile I think let's just use this one so that's 4914 let's do 14 so that should be transparent and then when it's time to respawn we will set it back to the red one and we'll turn edible if it's time to respawn here we can go self.tilegrid 00 equals the red one which is 49, 10, 11, 12 and edible false oops one thing we still don't have is the ghosts turning back to red on a timer right now it just stays blue until you eat it it does correctly give us the game over though which is good that means when we're turning back to the red ghost it's working properly when it respawns it's red so I think we're ready for blinking right I think we're ready for blinking how do we want to handle blinking I think we want to have an edible duration I'm gonna put on two seconds to start so first let's get it turning us back to unedible unedible after that delay is over so we actually need a self a turned edible I want to say editable turned edible at set to none let's make this a property instead of just having to set to variables so if we are becoming edible we're gonna update the turned edible at if now greater than self.turned actually we can say if self.turned edible at and because that will be none if it hasn't been set then we say turned edible at so now greater than turned edible at plus edible duration back to non-edible that's a false the time doesn't need to be updated but that's handled in the property anyway so yeah that should make it to where we turned back we need to go self.turned edible I guess actually let's do that in the property even that's even better nice okay let's go here so else if new val was false then we'll say self.turned edible at and we'll just do none that way it goes back to skipping that part of the logic since we did the and on there so we should have two seconds of time now where it's edible and I think I still ate it I didn't mean to do that no okay so yeah oh oh that's weird oh wait we didn't turn the color back we need to turn the color back we can do that in the property too actually let's do that in here so if new val if so if we're becoming editable then we're going to say self.tilegrid 0 0 equals blue which was 12 13 14 15 16 17 18 and then if we're becoming non editable we're edible we're going back to 12 and then we don't need to do this here anymore instead we just set edible true that's a property and it handles everything else for us which is super nice I like that a lot better I think I underutilized properties sometimes they're really really handy to make it to where when you change a state value oops you can also have some other stuff happening oh I think the big pellets spawned like basically where I took my first step so I think coincidentally it looked like the ghost started out as blue but I think what actually happened is the very first pellet I ate was the big one this time it started red it's going to get me so close here we go do we have 2 seconds yeah it turned back to red nice so now we just need the blinking before we go back to red we want to blink how long do we want to blink let's call that the edible warning duration we can always play with these times a little bit I think I'm going to make it 3 quarters of a second to start with and see how that feels so let's go and also set edible warm time equals none so then in here we're going to go edible warm time equals the current time which I'm going to put in a variable and then reference rather than calling time.monotonic multiple times to be called multiple times in a row then you could get different numbers so we say now is when we turn to edible edible warm time that's going to be now plus the value we just set which is edible warning duration no it's going to be now it's going to be now plus the difference of this minus this so our full duration is 2 seconds minus 3 quarters of a second means 1 and 1 quarter of a second after we turned edible that's when we're going to start blinking 1 and 1 quarter second later total duration minus the warning duration so okay so now you can say I don't think we care about the direction so if now is less than self dot edible warm time then we want to start blinking which we're going to do by setting current sprites to be a list of the sprites we want to blink between self dot current sprites equals the blue one and the white one which is 18 and 19 let's call it 18 that way our first blink is too white since we were on blue to begin with before we got into this state so we set the current sprites and then we just call well we need the animation delay what's the player animation delay 0.2 we go on the same speed as that maybe so that means 0.75 that means we're only going to get like three blinks alright let's call this 8 and then let's actually make this 0.1 and see if that's too flashy so we're going to actually say if self dot edible warm time and because we're going to set that back to none when we don't want to be blinking maybe we should be making more variables and kind of making some of these variables do double duty like it's a time and it's also a boolean for if it's supposed to be happening but so then we're going to say we need a last animation time is that happening inside of entity though no that only just changes the sprite index okay animation time none alright so then okay so we know we need to flash so we set our sprites we say if self dot last animation time plus ghost dot animation delay then we want to animate which means for us effectively called next sprite which is on entity which we're extending next sprite will basically loop between these two for us and then what we want to do is whenever we respawn when it's time to respawn we will set the edible worn time back to none which means then we won't be blinking anymore we're already changing the tile the sprite I mean technically I guess we could change these we don't even really need to honestly it's a little bit weird because it's going to be called current sprites but we only use it when we're blinking which means anytime we're not blinking we actually have this variable called current sprites which contains two sprites that we're not actually using which is not the best from a naming perspective may eventually change that I'm curious though for now if it works I'm always does it work first kind of a developer or look why does the OS need to create sprites yeah that was a question for the um from the youtube we got earlier about the OS right so we turned back oh I'm not going to make it I don't think it blinked though just went straight back to red maybe I didn't restart though unsupported types adding and none gametik 399 48 starts out on none so we can't add it to that so I guess let's start it out on zero where I suppose we could use timed out monotonic too but it's effectively going to be the same thing oh it seemed like it was blinking too fast it's going to blinking instantly doesn't actually go to blue so why is that happening they're supposed to stay blue until it's time to do the warning be 1.2 seconds after we turn blue this is backwards edible warm time is in the future when we set it which means that it's always greater than now when we first set it we actually are waiting for now to be greater than it we want to be waiting for that at least gonna get eaten there we go now we get blinks at the end oh it's not turning back properly to red anymore now so it goes back to it doesn't go back to red but it does go back to non-edible was there no big pellet that time yeah so it's back to non-edible because we got a game over I never knew you could make two hours out of this it seemed to be almost done two hours ago oh I mean there's still quite a bit to do it is it does sneak up on you as far as like once you get down into the weeds of a game like kind of spend chunks of time on things that seem small and innocuous while you're playing the game but for what it's worth I'll be back in the morning tomorrow so if anybody wants to see more one-dimensional Pac-Man action tomorrow morning at 10 a.m. central time over on my own channel I'll be back at it working on this I'm gonna see if I can't figure out this bit right before so we got a couple of minutes left here I'll try to solve this before I cut it out for the night I think why so it's gotta be so but we're setting this back to none right so we set that to none which then it should not trip this because once it's none then that will not be true so we won't go to here we won't call next sprite okay I think what happened is we we respawned we set this to 12 but I'm thinking maybe we tripped this like one last time or something really makes sense though right oh wait wait this is when we're respawning though that's not what we need we want when we stop blinking which is where it's where we set edible back to false which is we respawn right here but that should be setting it to 12 I mean the when we do actually respawn it's happening right here so I guess we could just do it twice I feel like it should take effect from inside there though I'm not sure this is actually gonna solve it yeah basically it just keeps blinking okay for one thing too though it gets messed up if you eat a new pellet makes it hard to troubleshoot basically a new pellet spawns and then I can't really tell what it's doing it looks like it just keeps blinking okay it's because no why is it not turning back to red have a question is it getting is is get it is get tick getting too long game tick I think you mean probably I know you prefer to get it working first but it's a bit complicated is it not I mean refactoring maybe it's not that long um I don't feel like it's too long I mean right now so like we have game tick for each entity so this is the ghost game tick and then we have the player game tick and then we have the overall game game tick which is what calls it on the other ones some stuff I will still move I think like right now the teleporting from edge to edge for the player is happening inside the game game tick which I think I'll move to the player game tick possibly some other stuff too but I think that's the main one um but after that I don't think there's really too much in any of them I think they all basically have almost you know exactly what they need to do there's some stuff like this where I'll probably delete some commented code in a couple of spots but I don't feel like there is too much to be changed around you could certainly rewrite it to be different but maybe a state machine might have been a simpler representation of this like a full on state machine with like here's the different modes but we effectively ended up with a state machine not with a state variable of a single thing but do we ever print that we're changing back to non-edible it does I'm gonna make it not spawn pellets so that we can try to not hit another special pellet just gonna make it not do this for now maybe that's why it's not changing back actually maybe I was just always eating another pellet means we probably need to tweak the times a little bit because it stays editable for so long edible that you almost are like guaranteed to be eating it because it's edible for long enough and you move fast enough that you can get across the entire map but it says back to non-edible and it ate me a game over so it so edible must be false but yet somehow we ended up on blue still so I don't understand why but I do think if we just added another thing in here that said if we are actually edible because we know edible is false and so if we don't call next sprite which is what's doing our animation between white and blue if we don't call that when edible is false then we should be staying on red because it seems like it's getting set to red and then my guess is that it was just changing sprite after that there we go okay and we got Aten there at the end we got our blinks we go back to red very good very good alright print the color state I think we got it that's going to be definitely my next step though is add more prints for when basically just every frame print what color it is but we made it there in the end alright I think I'm going to pack it in for now so I will bid you all farewell for this evening thanks for hanging out thanks for the suggestions questions and everything in the chat always appreciate that kind of stuff like I mentioned a little bit ago if you would like to see where this project goes if you're interested in this and you want to see more I'll be back tomorrow morning to work on it again that's going to be 10 a.m. central time over on my own channel I'll put links in the live broadcast chat which is the discord channel that we're in here when I get started yeah thanks to everyone for tuning in for hanging out I hope you all have a good rest of your evening and a good weekend and all that stuff I will see you tomorrow if you want to join in otherwise later on thanks folks