Coding Games on an ILI9341 SPI LCD and Touchscreen - Arduino

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
now that we know how to drive our lcd and touch panels it's time to make a game so let's code breakout [Music] hi and welcome to bytes and bits if you've watched the previous videos in this little series you'll know that we've been working with one of these little lcd touch screens and we've been seeing how we can both set it up program it create animations and then calibrate it so let's bring all of that together today by actually writing a little game so as you saw from the intro sequence we're going to be looking at breakout or arkanoid which is a little game where we have a bat down the bottom a ball bouncing around when it hits these bricks it destroys those bricks and that basically is the game so we're going to be using the lcd screen as our display for this game we're going to be using the touchpad screen input to control the bat and we're going to put it all together using our arduino so let's get straight into the code and work out how we do it so i've got here the breakout code now all the code in this lesson is available on my website so please check on the description on youtube and you'll see a link down there to the project page for this and again you'll get links as well from there to the various other videos in this series or need i'll i'll put a little link up in the top corner to the playlist for this particular video series but to say all this all this code is available for you just to download and bring into your arduino or ide and you can then test this out for yourself so the actual code itself then i'm going to take you through it bit by bit and really what we're going to do is i'm going to disable certain sections of the code so we can see each bit as we need to put it together and then finally build up the full game so the first sections of code we're going to look at is the real setup of the game and then producing the various game states so that so the start game the play game and the end game states so the first bit we do is to once we first turn on the um tft screen we need to calibrate the touch screen with that and again if you look in one of the previous videos you'll see that whole process taking place so really that's the very first thing that we're going to do so we're going to come in we're going to set up the tft we're going to set up the touch screen there's a number of variables we're going to be using throughout our code and one of um a block of those are to do with the calibration off that screen so we're really there matching up the values we get back from our touch panel with the pixel values for for the left and right and up and down coordinates so that is the handled so if i come down here a bit more we have a function called calibrate touch screen and that really then it puts a little a couple of crosses on the screen you have to touch those it then reads the values off the touch screen for these known screen coordinates and then calibrates the various values that we're getting back so that we can then translate coordinates coming back from our touch panel and then translate those into actual screen pixel coordinates at the end of that we end up producing a model for our touch screen which is based upon in effect two two straight line translations for our x coordinates and our y coordinates you can see here that we are we are modeling um our x coordinate translation function as a straight line and then our y coordinate translation function as well so if we actually have a look at the setup code so again this is the first bit of function that the system will run so we are doing our so from from here down to here is our normal initialization of our lcd screen and our touch panel so you can see that we are writing to the various control um select or chip select signals just to make sure that we don't have any um select con contention at the beginning of it we're then setting up our screen so we're setting its rotation so that we actually have it as a landscape screen and then we're filling it in black we're making a note of the size of the screen and we're then starting off our touch screen and there you can see at the end of the setup function we are calling this calibrate touch screen function which will go through the whole calibration process and and generate that model we then if we come down to our main game loop which is just down here i can find that for us okay so here's our main loop so again we are going to limit the frame rate um to 50 frames per second and again if you look at the previous videos you'll find out how we did that so basically we are logging this function melees tells us how many milliseconds it has been since the arduino booted up so we take a note of when the last frame ran we have a look at what the time is now and we don't let this frame run until at least 20 milliseconds have passed so that will give us a maximum frame rate of of 50 frames per second um if if our code is running a bit slower than that then yes we will get a lower frame rate but at least it means that we have some way of controlling uh making sure that the speed of the ball doesn't vary too much as we work around so that's what that first little bit here is doing and now we have our state machine then so we're storing the game state in this variable so this is a global variable game state so state one is where we're starting the game so so actual state one then just simply filters through into um state 11 which is where we um draw a little button on the screen so that's just drawing a button on the screen using um rectangles and text and then it's setting it up to state number 12 and state number 12 is where we wait for that button to be pressed so we put a button on screen saying click to play and then we loop ground in here checking if the screen has if the touch screen has been touched if it has been touched we work out if that touch point is inside our um button and if it is inside our button then we initialize our game and we then go on to game state 2. now the moment i have blocked out the initialized game function so that will just simply come into the play state it will wait for one second and then go to the endgame state so this is really just to try and make sure that we get the overall control flow of our code working so we'll turn the turn we should be able to turn the board on check our calibration put up a button click the button to start the game the game will automatically finish and we'll then finish back out down here in our end game screen state where we again clear the screen and we print up our score and put up a new button which basically says to play again and at that point it will go back to our game start state so that's really just a bit of what what we call sort of boilerplate which is really just what every game needs is a little state machine that helps control the game start when the game finishes then it handles the game finishing so we can then play a second game after that so let's upload that into our arduino and see what happens so that's compiling and uploading up to our arduino and once that's up and running we should be able to see our screen go into calibration mode so there we go and there's a little first red cross appearing so we click on that then our second one up here we click on that and that's the screen calibrated so it's now in the game start state so it's waiting for us to click this button once we click that our game would now go into its play mode and then of course at the moment we just simply got it sitting in the play mode for a short time and then coming out to the end game state where again we've got our button to play the second game and there we go and then back to our endgame state so that's the main control layer of the game written so most of the basics of functionality to get the game up and running and then get us to the various game states so let's start looking through the actual code to play the game itself uh what i'm going to do i i'm going to bring each block of the game code in bit by bit so we can see how it's all built together and you'll see how i've programmed it now i've actually programmed it using a mixture of procedural coding where we're going to use various functions to implement our blocks of code and then the actual game blocks the actual sort of bricks in the game are implemented using object orientated coding so you'll see the difference between the two and again it's up to you what methodology you use um i i tend to go more towards the object oriented whenever i program but as you see here it can all be done using procedural based as well but anyway that that's that's something you'd have a think about as you go through so let's start looking at this code so we know that after we've done our setup and so on we come down into our loop function and again that is part of the sort of standard setup for an arduino sketch and the loop function then is designed to be called continually so that our our code can run and then when the loop finishes the arduino sometimes has to do some of its own housekeeping functionality especially if you're using some things like serial channels and various other packages that they may need to have some compute time which tends to be either either side of the actual loop running so so your code you you don't really want to get it caught up inside this bit of code and and trapped there so you really want to sort of make sure that you let the loop finish and then come back round and then redo your next bit of code on the next loop update again that's not a hard and fast rule and if you know what you're doing then obviously um you might not need to let the loop escape back out and do something but anyway in this sort of game programming i'm using that again partly to control our frame rate here so we we need to let the loop function complete so that we can come back around and then limit our frame rate um but also then when we use our state machine it makes that a much better way of of implementing our game okay so this switch statement as i said that creates a state machine and the basic idea here is that when we come through our loop function runs the state machine is really executing a block of code trying to work out whether we need to move to a different state and then letting the loop finish so you can see here we come in at game state 1 and that simply then switches to game state 11 state 11 draws a button on the screen and moves to state 12 and at that point our state 12 is an area where we could lock the code here and have it continually looking to see if there's a button press but using the state machine what we can do is we can say have have we got a button press if we haven't we just let the loop continue so we don't lock the code up in here if we have got a button press you can see that we will do that we do various bits of code here so we're going to be having a look at this in a game function in a second and then we move on to state number two which is the play game state so we don't actually ever lock the loop up in this bit of code um but again that's how i program uh these games and so on and a lot of the software that i would write for arduinos i would write in the same way using a sort of state machine type process which which lets you take advantage of this looping structure in the arduino sketch but again that's entirely up to you you don't have to do that but let's start having a look at the actual then building up of the game so we said here that we do call this function init game before we actually get into the play game state so let's have a look and see what that does so let's let's go up and see where that is so if we come up here a bit and up to here right okay so our our init game function then so when we call that we will generally have been in a position where we've got a button on the screen so we're going to clear the screen by filling it in black we're then going to draw the various screen decorations so at the moment i have a little area at the top of the screen which holds the sort of level and score and so on so we're going to draw that boilerplate text and so you can see here it's drawing a line it's then drawing the word score the word lives on the word level and again if you if you remember back to our previous tutorials on on using our lcd screen using this spi interface we really don't want to be redrawing bits that we don't need to redraw so these um these screen decorations are actually fixed so we only need to ever draw them at the start of the game and after that we can really just assume that they are there and and on screen so we don't need to redraw these every loop okay the actual numbers which go after these obviously we will have to redraw those but not the actual boilerplate code itself we're then setting up a number of bits of our variables so we are setting our our bat y position so really what we're doing here is where we've got a number of constants defined so if you remember when we went through our setup function we did log down what size of screen we were working with so we're getting it to automatically adjust the layout of our screen depending on the actual screen size that we find ourselves on so that's all that's doing here saying that the the the y position of the bat so where the where the bat is um vertically on the screen which is going to be down near the bottom of the screen we're basically saying we're going to use the screen height we're then going to take off the bat height and the little 30 pixel margin so that will put the top of the bat 30 pixels above the bottom of the screen we've then got a number of other global things like our our lives left our score and which level we're on and then i have three little functions here which allow us to add the actual numbers in the correct positions after these blocks of text and again by pulling them out into their own little separate functions it means that when we need to update those values we have that code already written so again the code for putting the lives number on the screen is is written once and we can use that from various positions then so once we get that information on screen then we eventually will call this function called init game board which is defined down here and we'll come back to that in a bit um because that is going to involve setting up our our actual bricks and so on but for this first bit um now that we've gone through the sort of initialization function we're going to look at how we can get the bat moving because that's the very first thing which we need to do in our game so if we come back down to our loop function and into our our state which handles the game playing you can see at the moment i've commented out the actual um code here so remember we said where we're using our state machine so when we're in state number two we this block of code will be running each time that we come through our loop function and that something creates an effect one frame of the game so if we comment out our our move bat function what that does then if we have a look up for our move bat function and that is up here okay so we have our move back function and really what we're doing is we're going to be using the touch screen to control the x coordinate of the bat so the bat is at a fixed y position and we saw that being set in the initialization code but our x position then so really when we call function move bat again we're going to really test has the screen been touched if it hasn't been touched we're going to quickly get out of this we can carry on doing the rest of the code but if we have got a screen touch which is that this if ts.touched then we're grabbing hold of the touch panel coordinates we're then converting that into screen pixel coordinates and we're then using that x coordinate to set the mid position of the bat so this line here is really just saying um of course we we have to signify the top left corner of the bat so our x position is going to be the touch point minus half of the bat width we're then just doing a bit of limit checking here so if the bat has gone off either edge of the screen we need to then just fix it at the edge of the screen and then in this block down here so this bit here we are continuing this idea where we want to minimize the number of elements that we're redrawing on each game frame again two to make sure that we that we have enough time then to actually do all of the animation and so on so if if the bat hasn't actually moved by very much we're basically saying don't bother redrawing it we are tracking where the bat is okay so we we have to make sure that we do monitor all the movements on that but we're really saying is if if it hasn't moved by more than four pixels then then really the user isn't really going to notice that so let's not bother redrawing it and that just saves us just a little bit of time because again the the slowest part of this code is when we have to communicate various items being drawn or removed from the screen so let's let's have a look and see if that actually works this is our move back function and let's upload that into arduino and see if that actually does anything so that's going up into our arduino and there we have it okay so we're now in our screen calibration function let's just calibrate the screen and then we're in our start game state where we have the button we click on that and we now have our sort of boilerplate text now in place where we have our score and again the the numbers after then are being driven by those three little display functions and we should now if if we touch on the panel here we should then have a touch event and then our bat should now be attached to our pointer okay and there we have it and we can see if if i move it here if i move just make very slight movements you can see the bat doesn't move until we've actually moved it a certain number of pixels away so that just means that again we're saving a bit of time there on not redrawing the bat every single loop okay so that's the that's the bat in place now so the next code we need to get up and running is the ball so really that's going to be a little circle which is going to move around the screen so let's have a look at how that is brought into the code now as usual i've been working through a number of the variables so we've defined a number of variables at the top of our code here and these again are going to be global variables just to make it easier for us to use them throughout our code and you can see here that we have then a number of variables which are to do with the um position of our ball now we're actually going to use those as not as integer variables we're actually going to do them as floating point variables you can see here i am defining them with a decimal point at the end of them and we'll see why that is in in a second um but if we go down to our game loop then so down somewhere down near the bottom right so we know that we're in game state two we where we are playing so i've uncommented a couple of bits off our code so we now have a move ball function and i've also uncommented the check hit bat function which actually means um that's the bit that actually checks obviously if it hits the bat so we'll let it bounce back up again but let's have a look at our move ball function then so if we come up here and we have our move bat function and up here somewhere we will have our move ball function so move ball function so the move ball function now so we have these floating point variables which dictate its x and y coordinate values and by using fluid point variables that gives us much finer control over both our position and our velocities if we just simply use integer variables then obviously the the smallest um measurement unit that we can use is a single pixel and if we start talking then about um velocities so how many pixels per loop is moving in the x and y directions we can only go into your values of one two and three by going to floating point values then we can obviously have much finer positioning information and our velocities as well we can have it moving half a pixel and the idea here is that when we try and then plot that on our lcd screen we will simply just round our floating point values to the nearest integer value so although we are tracking and moving in very fine floating point variables um the actual positioning on the screen will still be fixed to its normal rigid individual pixel values that just gives us extra control so we're modeling our movement at a fine control level but of course we are displaying it at the integer value pixels and again that's a technique you'll need to use throughout your game coding where you need to have your models running at the correct level of accuracy to give you the correct motions and and responsiveness that you need for your game and then of course you can simply convert those um really accurate values to less accurate values that are needed in your actual display of of what's happening in your game so it's just a general sort of um idea there so basically here is then that we have a an x velocity and a y velocity for our ball and that just simply tells us how many pixels it will move per game loop in the x and y directions so we simply then once we come into our move ball function we simply then update our x and y coordinates we're using here a a new xo in fact what we're doing is we're saying the the current position of the ball is given by the values x pause and y pause we're then working out where the new position of the ball is going to be in these variables new x and y so so at this point after this so we will then obviously then limit check our new x and new y values but once we get down to this point here we now have a x pause and y pause which tell us where the ball was the last time we drew it and then new x new y which is telling us where we now need to draw the ball and that's when we need that to be able to do this over drawing technique so what we're simply doing is we're saying we have a function here called clear old ball position and that simply draws a black circle over the top of the ball's previous position we're then drawing in this line here we're then drawing the new ball in using the new x and new y position so that will then have moved the ball and then we're saying okay our last x position and our last y position are going to be a record of where we've currently drawing it and we update then our x position and our y position so we now have the idea where we are both keeping a track of the last position we drew the ball and then working out the new position where we're drawing the ball and using that then to do this over drawing technique there are other bits then what we've just uncommented so we do have that check um so in the move back then we have a check hit bat function so that is really just looking at the ball so we have here um the ball is coming down we know that it is in a position of y pause and x pause and we have i'm using this check collision function here um and that is what's known as a binding box collision detection so we're simply saying here we have a bat which is a rectangular object we have a ball which is in effect a rectangle we can put it inside a square so we we it's all we're doing here is we're making a square which is what's known as a binding box for that circle and then we're just working out this this check collision function will come back telling us whether those two rectangular areas are overlapping and again i've i've done a whole video on binding box collision detection and i'll put a little link up in the top corner that you can have a look at that um but basically it's looking at the various edges of each of these boxes and there's a certain set of conditions that we can check for and that will tell us if those two boxes have hit each other or not so really then saying is if if we are getting a hit then we are going to alter the velocities of the um ball to take a kind of that and so really saying here then so if if but if we have a hit then we are on the bat so remember this is checking for the bat so we know that if we hit the bat we will always bounce back up again but what we're also doing here is we are gradually for each time we hit the bat we are gradually increasing its velocity so as as we play the game the ball will actually get faster and faster and faster up up to this maximum velocity of 5 pixels per loop and you can see there so that's us in increasing the y velocity and limit checking it to five and that's us then reversing the y velocity so if we hit the bat we simply just reverse the y velocity and that will send the ball back up again we're also on our bat we're not just having our bat um be almost like a flat surface we're going to model it as not so much a curved surface but a non flat surface so if we hit the if the ball hits one end of our bat it will bounce off at a different direction to when it hits this at the center of the bat so basically if we hit the center of the bat then the the ball should do a sort of a normal reflection where we don't change the speed it's traveling left and right whereas if we hit the bat away from the center then the further we are from the center of the bat then the more we sort of model in in effect a a curved bat so it will deflect at a at a different angle um basically then so if if if our ball is traveling from left to right and it hits the bat on its left hand edge then it's almost like it will be it will be sort of hitting a a a a a self-reflecting angle so when when it bounces off the bat its angle will be less whereas if it was to hit the right hand edge of the bat so in other words um the the sort of curve that's curving away from its motion then obviously it it will go with us at a at a shallower angle and to the horizontal um and again the best we made is when we run this i'll show you that working but in effect what what this is doing is it's modeling in effect a a curved bat surface so we get our different angles and again this is where we need to have our our velocities and positions modeled as floating point values so that we can get a much finer tuning off of these angles and then the last thing there is we are making sure that the ball is not hitting the bat so what we do here is is once we have detected the ball hitting the bat we want to then make sure it is now clear of the bat so otherwise what would happen is that once we come through this function once we would detect a hit and alter the alter the um direction of the ball if the ball hadn't moved off the bat by the next time we came round again then of course we'd have a second hit so we simply just say once the ball has hit the bat we then place the ball above the bat which is what's happening here and then the ball will then no longer be logged against the bat and will travel on its merry way back up the screen so that that's that's really what we've brought into play at the moment then and obviously there there are other sort of various limit checking on the ball itself um but let's have a look at that working and we should be able to see the the ball bouncing around the screen and we'll then be able to check and you'll see this rounded bounce effect taking place as well so that's going up now to the arduino give that chance to come through that's initializing our screen then and so we start off with our calibration function so let's just calibrate that and then go into our play state and we now have our ball coming down and again bouncing off our bat so our bat now controlled by our our touch screen and our ball then onto our software control and you can see now that we are modeling this idea of the curved surface on our bat so if i'm able to get that hit just one of the edges there you can see that in the center our ball really reflects as if it is a flat surface but then if i hit at one of the edges you can see we get that curved effect and hit that edge again it curves off to that side as well so that gives us now our bat and our ball being modeled and we have that again that idea of that curved surface so the last thing we'll need to look at then is getting some blocks on the screen so the blocks in our game we're going to model those in a slightly different way so so far all of the code we've been written has been using just individual little functions and this is our procedural base coding and and that is perfectly fine and you can see here that we are breaking up all the little blocks of our code into nice neat little functions and each of these functions should be quite small and just handle a particular part of our code so you can see here our hitbat function just handles that hitting the bat so it's a nice succinct bit of code and we simply then piece together lots of these little blocks of code um to make up our actual our actual function but um our usher's program sorry but if we have a look then at how we're going to model our um blocks so in it in our init game function which is which is just here we've seen that we come through and we eventually create the sort of the boilerplate layout for our game and then we have this in a game board and this first bit of it then is just setting up some various parameters including our ball information but we then then come down to this block here which i just commented out so let's uncomment that and this then is a a a a set of four loops so what there's two one one loop inside another so we have rows and columns and these are going to produce our blocks and really what's happening then is we're saying here that we have five rows of 16 columns and once we go through there we're simply saying so our our first two rows are going to be blue blocks and they're worth 50 points magenta blocks 30 points and our last two rows are going to be yellow blocks worth 10 points and again that's going from the when this is drawn out it'll go from the top of the screen down to the bottom of the screen and we're then for each of those blocks so each of these times through this loop we're then going to create an object and we're using this idea of a block object which i've created i'll have a look at that in a second really what we're doing is we're creating here we're creating a two-dimensional array and again this is defined um up here somewhere i'll i'll we'll see in a second as one of the global variables but that then is in effect an array of arrays which gives us this two-dimensional so we can then access those using a row and a column coordinates so inside each of those elements in that two-dimensional array we are generating a new block object and we're giving it this information so we're telling it um in effect it's it's x and y pixel coordinates so we're basically saying that um each block is going to be 20 pixels wide so we're going to have it so it's column position times 20. we'll give it its um x coordinate um the the rows then are going to be 10 pixels um high and we have an offset then of 30. so that's just really just making sure that that in effect those two little equations there will tell us that for for a block at column and and row positions this is the pixel coordinates off its top left corner we're then setting its um actual size so we're using 19 by nine which is just one less than this and that would just then give us instead of having solid blocks of color this will mean that we have a one pixel gap between each of the blocks so we can actually see the individual blocks then we're then sending in a color value so you can see here the color of the block is given by one of these coming out of our switch statement and then the score or in effect the value for whenever we hit that block is again coming in here so this so let's let's have a look at this block object which is defined somewhere up um here a bit further um so you can see here this is where we're defining our um two-dimensional array so it's a so we're creating uh a variable called block called block sorry which is going to be so each element in this variable is going to be one of our block objects and then we're creating that as a two dimensional array with with five rows and sixteen columns but if we often look at the actual block itself so this is where we are defining our rc plus plus class so again we're saying here class block then we're defining its public properties so we have an x and a y and those are going to be the top left corner coordinates of it a width and a height which of course is width and height color and score and then a final one here which is going to be a a boolean value so that so these are the attributes or the class members of our of of our object is active is really going to tell us whether this particular block has been hit or not in other words is it actively being shown on the screen or or or should it not be drawn so when we create classes inside our c plus plus we have to generate a number of um methods for that and one is the constructor method and to allow us to do this sort of statement where we are creating a an array which is going to hold eventually block objects or instances of our block class we need to have a default constructor because that sort of will will be something which this needs to do so we're not yet control calling its main constructor so that that's the constructor that we're going to call when we instantiate an actual block object but we need a default constructor so that our our code understands how how to reference our block classes okay so this this is needed for to allow us to define arrays based upon this object okay we then have the actual constructor value itself and we've seen then that if we come down to our in a game board we can see here that this is where we are instantiating an actual object so that we'll call its constructor and for that constructor then we need to pass in the information that defines this block and we've already seen how that's generated so that if we come back up then to our our class definition you can see here that really we are are catching these um set up parameters for this particular block and then really just draw storing those into the class attributes so again so these so these x and y values are actually referencing these class attributes which we defined up here so we're really saying then so the incoming x position which is going to be the um left and right position of the top left corner we're simply storing that in the x chord in the x attribute then the y part incoming y pause is stored in this y value width height color and score when we first instantiate a block it hasn't yet been hit so we're saying that this is an active block and then we're calling one of the block classes methods which is draw block so again the idea here is that when we create a class this is going to model a block on the screen and it should also then have built into it the code that handles that particular object so for a block it needs to know how to draw itself so here we have our draw method so when we first instantiate the block it draws itself on the screen and that's just drawing a rectangle in the correct place at the correct size in the correct color and again all those values have been sent in in this constructor itself our block then needs to be able to tell if it has been hit or not so really it's it's it has this method then is hit and inside that we will send in in in effect a rectangle which will be in it which will actually be generated from the ball itself so we have a top left corner of a rectangle and then a width and height of our rectangle it's then going to use this function called check collision which we've written around here and again i'll have a look at that in a second and that really then just brings in um the ball rectangle so again that ball rectangle then is being sent in through these parameters so we're using that as one of the rectangles in our collision detection the second rectangle in our in our collision detection is of course the block itself so we're using the x and y coordinates which is the top left corner the width and the height and then this function back will then send us back either a true the ball has hit this block or a false that the ball has not hit this block and we're simply then returning those back out and and this this method will be used in our ball and block collision detection elsewhere to decide when the ball has hit a block when when the block is hit then we will ask the block to remove itself from the screen so this is where this function in here again so so we're really delegating all of the code which relates to the block we're actually building that and embedding it inside our class so this is the day of encapsulation where this block now we can in effect give it command so we can ask the block to draw itself we can ask the block to remove itself from the screen and we can ask the block are you being hit by this ball coming in so that's sort of again a a different technique of building your code and again for me i i i'm a fan of object-oriented coding because it lets me say okay here is a particular object which is part of my code and i can then encapsulate all of the data and the code that handles and models that object inside this class and then i can create multiple instances of my class so i can instantly have um however many blocks if so five times 16 blocks all handled on the screen and i can individually address them and ask them questions and make them do things so if we come back down to our init game board function so this in a game board function then at the moment then it's going to go through it's going to run through each row and column and put a block in each of those rows and columns and as we've already seen then part of the constructor for generating or instantiating a block object is for the block to actually draw itself on the screen so we've got that code now uncommented which means that if we now come back down to our loop function down the bottom here so once we come through here and we in it our game at that point we should now get a set of blocks appearing on the screen okay we haven't yet included the code here which checks for us hitting a block so you can see here we have a little function call which will eventually get there but we should be able to upload this and actually have our grid appearing on the screen so let's put that up into the arduino and get that uploading in there and there's our screen coming through so again we go through our calibration function into that and there we have our blocks appearing on the screen now to say it again the blocks are simply drawn onto the screen once those block objects are instantiated so as our ball is moving around it's not yet detecting when it's hitting blocks so it's just being animated and moving all the way through them up to top of the screen and you can see there that as the ball path is being drawn and again it's being drawn in terms of rubbing out the old position by simply putting on a black circle and then drawing the new position you can see that we are getting then these tracks being left through our blocks but at least all of our blocks are appearing on the screen in the right position in the correct colors and those are being put on there using these objects so instantiating objects off that block class which then understand where they are on the screen how to draw themselves eventually how to detect collisions with the ball and then how to remove themselves so let's let's have a look at how we can get that block of code then working and get our game heading towards its end state so let's look at getting the ball hitting these blocks so if we uncomment this next line you can see you already have written a function there called check hit block and of course that's going to check if the ball is hitting a block so let's come up here and see what that does so uh so here we have it this function basically then needs to loop through every single element in our blocks array so remember we have this array called blocks which is a two dimensional array with rows and columns and each of the elements is what an instance of one of our block objects so here again we have our our row loop with an inner column loop and that will of course get us through every single element so we're checking each individual block then so first of all is that block active so remember we added uh an attribute into our class which told us whether that block had already been hit or not so we're only wanting to check blocks which have not been hit so blocks which are active and the remember we added a method to our block class is hit where if we give it a rectangle it will check to see if that rectangle is overlapping with the actual block itself and that will then tell us that this whatever rectangle we pass in and you can see here we're passing in the actual ball itself remember where we're converting the circle of the ball we're putting it inside what's known as a binding box we're passing that into our is hit method and then we will do a collision detection between the rectangle of the block and a rectangle or the binding box off the ball so if the if it's active and it's being hit by the ball then we simply have to again we're calling a method inside our block class which where we can get it to remove itself from the screen we're then incrementing our score so remember each block has built into its class what its value is when it's hit so we're increasing our player score by the score value of this particular block we're then redrawing our score up in our top um boilerplate area and again by bringing that out to its own separate little function we simply need to call that and that will automatically handle deleting the old score and adding in the new score and then we're simply reversing the y velocity so in other words if we had been traveling up this will make it travel down but it also means that if our ball has got above the blocks and is traveling down to hit the blocks it will then bounce back up towards the top of the screen again so we just simply need to reverse the y velocity and we then want to actually leave this function so the next line here is it so if we have detected a hit on a block we actually want to return from this function and not bother checking any other blocks we're just checking for the first block that we notice the ball is hitting and really that then saves us from because we if you think about it if the ball could be heading right between two blocks and at that point we will get the situation where it will reverse its direction for the first block and then reverse its direction for the second block so really we just want to as soon as we have one block being hit at that point the ball will bounce and then we need to get out of this loop if it then continues to hit a different block it will then be detected on the next loop round on this but we're just detecting one collision per loop and then we return back from that function so you can see here that that is quite a a simple function again all of the complexity of how to work out whether a block is on the screen whether it's being hit by the um ball how to get it off the screen what its score was all of that complexity has been encapsulated inside our block object and that's one of the real reasons why i like um object-orientated coding because we we can do that once i've worked out how a block calculates if it's been hit or not we can bury that inside the class and then we can just simply ask that class or that object are you being hit and our code out here it it almost makes english sense okay so we can we can simplify our code out here to make these sort of um global so or or more english like method calls and method checks which just really makes once we come back in to look at this code um it makes it just more easy for us to understand what's going on and if we have to delve into how it actually does remove itself from the screen we have that nicely encapsulated in a nice little method inside our class so again if you're not familiar with object orientated coding i i do advise it is well worth getting yourself familiar with it because it does greatly help improve the speed at which you can code and the and reduces the complexity and effect but anyway so that now should give us the idea where we can now hit the blocks and have the remove from the screen so let's again try that out and see if it works so let's put that up to the arduino that's going in and we should get into our calibration function now so let's calibrate the screen click to play and we should now be able to hit the blocks and there we can see we now have our actual game taking place okay so we can see that that's not working and really all that's left now is to do some of the sort of housekeeping um on our game actually getting towards an end state so we need to now look at what happens when we've hit all the blocks we need to work out what happens if we miss the ball and really it's just a bit of tidying up now but the main block of the code is now written so let's come back down to our main game loop which is in in state two down here so we've got the ball back moving checking hits on the bat and the blocks so let's have a look at what happens when we miss the ball then so this is the next block of code i have in in this listing so if i do that and let's just leave this last bit down here i'm still commented out so we need to check if well if if we have lost a ball so that's really just if the ball has now gone off the bottom of the screen so if i have a look at this function check ball lost so if we come up here so that should be around here somewhere check ball lost so again that that's really quite a simple function so if the y position of the ball is greater than the height of the screen plus the ball size so it's it's actually gone right off the bottom of the screen then our ball hat we've missed that ball so remember our our bat is a certain number of pixels above the bottom of the screen but we don't want the ball to signal its loss just as soon as it goes below the bat we actually want it to physically disappear off the bottom of the screen to make it look like the ball really has disappeared away from our from our our play area so that's all check ball loss does a very simple function we come back down to our game loop then so we're saying so if the ball has gone off the bottom of the screen then we've lost one of our lives and we then update the display on the screen so remember we're only updating these numbers when we need to again to reduce that number of draws out to the tft we're saying then if we still have some lives left so if we've got greater than zero lives left then we're going to create this function called new ball and that's going to then restart the ball for us so if we've lost all of our lives then we go into our end game state which is state number three which is down the bottom here and again we've already looked at that and that just simply puts up uh your your final score and then effectively then goes back up to the start screen where it asks you to click to play let's have a look at this idea of new ball so if we come up here and look at new ball which should be not too far from there right so again when we create a new ball this is very much the same as when we initialize the game so we put the ball back to its starting position which is over at the left hand side of the screen and below the blocks we reset its velocity so we're traveling at 45 degrees and then we call the move ball function to actually put it on the screen we then have a delay of 1000 so again that gives the player a chance to notice that the ball has respawned and get back in position to get it if we didn't have a delay there um if if you had just missed the ball um it it gets a little bit difficult to then get back into the game and and get right back in to get catch the next ball so that just makes it a little bit more playable but you can see it it's very simple because we know that the positioning of the ball the drawing of the ball is handled by this function are creating a new bulb just simply needs to reset it's the values that model that ball and then our functions will take care of that so let's have a look and see if that works so we've now got that coming up into our arduino and there we into our calibration screen so we calibrate that click to play we can now play our game if we now miss the ball that comes up it resets pauses and now we have our next ball and again we've lost one of our lives so again if we lose our life lose a life and we're now into our end game screen where we see our score and we can of course click to play again okay so that's all up and running so pretty much the last thing we need to look at then is what happens when we've hit all of the blocks if we come down to our main game loop again so you can see again if i just uncomment this last little block you can see again we have another function which is going to do this check for us and again by breaking all of your code out into functions it means that your main game load you can see here again it makes much more sense as you read through it so if all blocks have been hit then we go to game state four which is going to be setting up the new blocks and you can see here that that's putting in a delay it is then leveling us up and again the level um is being drawn on the screen again using another one of these little helper functions the level itself then we've already seen that at certain bits to do with the game speed and so on the ball speed and so on that level is used in there and then we have our init game board which again is our little function which we broke out which resets the ball and resets the blocks so we've already been through that but let's then have a look at this new function and check all blocks hit so if we come up here to have a look at that one so checks again so check all blocks hit and really all we're doing is we're counting how many of our blocks are active well we'll actually we're just really looking to see if any of them are active and as soon as we find an active one we will return back false we have here our loop which is going through our our rows and then each column in each row checking the each of the elements in our blocks array and asking it if it is active if we find an active one then we obviously haven't hit all the blocks so we can return back false from this function if we get to the very end of our double layer loop then that means that we haven't found any active ones so we can return back the value true which says that all the blocks have been hit and at that point as we've already said then if it's if it comes back with the true value we know we've had all the blocks so we simply then generate our new game screen and off we go again so again let's see if if that works well well actually um if if i do it at the moment we obviously have to hit all the blocks so what we could do here is have a look at our init game board and just modify that for our testing purposes so let's come up here and find our init game board function which is near the top so here we are so init game board and we go through right so we go through every single row every single column we make a block in that column and we know that when we instantiate our block it sets it to be active so all i can do in here so i could say so if row is equal to so if we're in row um let's say row zero and column is ro um so we'll put a a block at the top in the middle so we want to select that block and we obviously want to select the inverse of that so if i do um so if not that okay so so this inside here we want to select row 0 column 10 and what i'm going to do is i'm going to say so if it's not row 0 column 10 then what i want to do is i want to say so blocks row column dot is active equal to false so setting its is active to false will indicate that this block has been hit but of course if it has been hit we need to make sure that we take it off the screen so we simply are need to do our blocks rule column and then we will call its function so remove or its method remove block so that means then that once we come through here we are filling up our blocks array with instances of our block class so we need each of the elements in our array to be an actual block otherwise our code will fail later on when it tries to access that block but then we're saying that if if it's not this single block that we're looking at then we want to mark it as being hit and take it off the screen so in effect what should happen at the end of this function is that we should only have that single block on the screen and that being the only block which the system thinks is actually still there and that just makes it easy for us to test this final end point where we hit all the blocks otherwise we've got to play the whole game all the way through every single time we want to test this function so let's see if that works so that's coming up to the arduino went to our calibration screen there we go and we click play and we now have a single block on the screen there so hopefully if i can at some point manage to hit this there we go we can see the screen being you can you can almost see the screen redrawing through those blocks and we can of course then we can see that our level is increasing on each of these screen clears and obviously we are back then to playing our game okay well that's the whole game complete now um so hopefully you've seen then as we've gone through there we we're using our lcd screen and our tft touch panel so hopefully you can now see how we can start to use that for user input and again here using it as a game controller we've got the lcd screen and hopefully you've seen some of the techniques there where we're trying we're simply trying to reduce the number of times we have to send information to that screen again going across this serial spi interface um especially with just the the single data connection that that is really the slow part of this whole system so the the fewer times we have to draw information on the screen and the smaller those pieces of screen that we're drawing are then the faster we can get the frame rate going to give us a smoother game and again here the idea is that we are only drawing basically we're only drawing the bat and the ball so the bat's only being drawn if we move it more than about four pixels the ball is the only really thing that's being drawn every single frame all of the blocks are drawn once and then just simply removed whenever we hit them so that gives us then our our frame rate so hopefully that gives you some ideas as to what you can then go on and do with your own coding and and hopefully have some fun with that so i hope you've enjoyed this tutorial please make sure you visit the course page all the links in the description below to get the code and more information about this do make sure you subscribe to the channel and like the video and hopefully i will see you in another tutorial so bye for now don't forget to visit the course pages for this project there you'll be able to download the code for this lesson and get lots of extra hints and tips you'll also get access to all my other programming electronics and gaming projects all the links are in the description below for more games programming electronics projects and retro gaming please make sure you like this video subscribe to my youtube channel and visit my website
Info
Channel: Bytes N Bits
Views: 11,177
Rating: undefined out of 5
Keywords: ili9341, spi lcd, touchscreen, arduino
Id: Oh9vgomyuOI
Channel Id: undefined
Length: 65min 46sec (3946 seconds)
Published: Sun Jan 03 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.