Apps From Scratch
Lección 3 de 4 · Build Your First Flutter Game | Apps From Scratch · Programación
Build Your First Flutter Game | Apps From Scratch
hey everyone welcome back to my channel where you learned how to create amazing apps with flutter and Dart today I'm going to teach you how to
build your first mobile game with flutter and a minimalistic game engine package called flame before I get started I want to let you all know that you can get a discount on algo expert by
going to Hal go experto slash Marcus algo expert is a coding interview preparation platform that has a curated list of interview questions each with a comprehensive video explanation I highly
recommend algo expert as I've been using it myself to prepare for my technical interviews and it's been very helpful if you're interested make sure to click the link in the description to get a
discount with promo code Marcus and with that let's get started as always let's start in our pub spec Tom will file where we're going to import the flame and shared preferences packages flame is
the minimalistic game engine that we're going to be using and shared preferences is going to allow us to store the users high score locally on their device let's go to our main dart file and we're going
to start by clearing out everything in here so we can delete all that and we're gonna make the function void main in this function we're going to need to create a util which is from flame so
let's make util and import the flame package will call it flame util equal to util and instantiate that next we're going to need to do a wait flame util dot fullscreen and because we're using a
wait and our main function we need to make it a sink fullscreen ignores the status bar at the top of our app next we need to create flame util and we have to set the
orientation to device orientation dot portrait up and that's going to keep our device orientation blocked in portrait mode and we need to import services start now if we hit save we get an error
because we need to implement the run app method so let's type in run app and inside here we're going to need to pass in our game controller dot widget so above this let's create game controller
game controller and set that equal to game controller now game controller is going to be a new file so inside our Lib directory let's make a new file called game underscore controller dot dart our
a game controller dot dart will extend game and game is from the flame package and the game controller needs a constructor so let's make that it also needs three more methods the first one
is going to be void render canvas see the next one will be void update and that'll take in a double T and the last one will be void resize sighs sighs and then make sure to import dart UI it's
safe and going back to main let's import our a game controller it save and we can remove the test directory here because we're not testing any widgets so I delete on that let's it save now and not
restart the app and we can see that we have a black screen our game controller is the brain of our game it contains all the instance variables and logic behind our game which we can display on our
screen using the render method we can also update our game using the update method which takes in a time and we use the resize method to set the initial size of our screen let's create two
instance variables to track our screen size the first instance variable will be size screen size and the second one will be double tile size the screen size will be the dimensions of our screen and the
tile size will be a smaller portion of our screen the reason we need a tile size in our game is because when enemies move on our screen we want them to be able to move at the same speed no matter
what screen size the user is playing on let's set up our resize method we will set our screen size equal size and then our tile size will be screen size dot width divided by 10 so the size of a
tile in our game is a tenth of the width on any screen we need to call our resize method inside our constructor so let's make another method called initialize and we'll put it right under our
constructor boid initialize and it'll be an async function and inside here we just call resize oh wait flame dot util dot initial dimensions and that's going to get the
initial dimensions of any device run and we just need to import the flame library and spell initialize correctly now before we start coding any features of our game I'll explain how the game works
the idea is that you're the player in the center of the screen and you have enemies spawning all around you in order to kill the enemies you have to tap on them three times and if they touch you
they're going to deal damage to you so we'll have a health bar at the bottom of the screen which will decrease as enemies do damage to you and the more enemies you kill the higher your score
is the goal of the game is to just survive as long as you can and kill as many enemies as possible let's create a background for our game inside the render method we're going to
create a right background and set that equal to rect dot from L T WH which is left top with height and the rect will be from zero zero and the width will be screen size dot width and the height
will be screen size dot height now we need to set a color for the background and the way to do that is by creating a pink we'll call it background paint equals paint color and set the color
equal to zero x FF fa-fa-fa and all we have to do to draw the rectangle is do C draw rect background background paint and it safe and now we change the color of our background we set the left and
top of our rectangle to 0 0 because our canvas is drawn from 0 0 up here and then the bottom right is screen size not width and screen size dot height now let's work on making a player spawn in
the center of our screen so let's go to our live folder we're going to create a folder called components inside components we want to have player Dart flare that dart will be class player and
we need to create the instance variables that the player will have the first one will be final game controller game controller make sure you import the game controller we're going to have a max
health we're going to have current health a rectangle for the players hitbox and a boolean called is dead which will set the false our player needs to have a constructor and our
constructor will taken this dot game controller and this will assign the game controller we pass in to the final game controller instance variable we have in the player now the first thing we want
to set is the max health and we'll set that to 300 and we'll set the current health equal to 302 and since we're setting our max health and current health to the same variable we can
actually put this on one line so we can say max health is equal to current health equal to 300 and that's up to both variables to 300 next we want to define the size of our player so we'll
make a final variable called size and set that equal to game controller tile size and if we remember we set tile size to a tenth of the width no matter what the screen sizes and will make the
player 1.5 times the size of a standard tile now we want to set the player rect and we need to import dart UI so player rect is equal to rect dot from left top with height and the left will set it to
game controller dot screen size dot width divided by 2 since we want the player to be in the center of the screen so by doing what divided by 2 we're now in the center and now we need
to vertically align so a game controller dot screen size dot height divided by 2 and then for the width and height here we'll just set that to size now let's it save and we need to write two more
methods to have our player render on the screen the first one is render so avoid render and I'll take in canvas see and the second one is void update double T will create a paint variable we'll call
it color paint and then we'll set the color equal to color 0xff and then to make it blue we'll just make it zero zero zero zero FF and then we just have to draw the rect on the canvas so C draw
rect will draw the player rect and assign the color will leave the update method blank for now and just hit save and go to our game controller inside our game controller we need to make another
variable it'll be a player and we'll call it player and make sure to import the player dart component now inside initialize we need to set the player
equal to a player and it'll take in this this represents the game controller and we need to pass that in to our player because we ask for the game controller and that's that's the player's game
controller here now it's at save and then to render the player we need to go to our render method and all I have to do is type in player dot render and pass in the canvas let's hit save and hot
restart the app and now we see that our player render on the screen but we can clearly see that the player is not centered even though in our player that dart we said
game controller dot screen size not width / 2 and then also the height divided by 2 which we thought would be the center what would happen is that we set the players top left to be the
center of the screen so this point right here is actually the Sun of the screen and the way we can Center the player center is by going to our left top here so the left always do is subtract size
divided by 2 now if we hit save I'll restart the app we see that the player moved over to the center of the screen but we need to fix the height still and that's the same thing we have
to do so size divided by 2 here it's Save & hot restart the app so the players range Stan she ate it and now the players in the dead center of the screen so whenever you're trying to
render a component on your screen and you want to be centered or at a certain point make sure to subtract the size of your component divide it by 2 so then the values here represent the middle of
your component another thing to note is that the order you render your components in matters because right now our render method renders the background first and then after that we render the
player so our player is on top of the background if we were to take this player dot render and put it behind the background and now it save we see that the player disappeared and if this
happened you make sure that you put the player after if we comment out the background draw rect it save we see that the players here so what happened was that the player rendered first and then
we rendered the background on top so make sure that when you are creating components and rendering them that you order them correctly next let's deal with taps on our screen because we want
to be able to tap enemies let's scroll down and underneath our resize method let's make a new function called void on tap down and that will take tap down details D and for now let's just print
out D global position it's save and when we click on the screen we can see that our debug console prints out the position we're clicking and for safety we want to bind this on
top down method to our game so let's go to our main dart file and underneath run app we're going to make a tap gesture recognizer tapper equals tap gesture recognizer will set tapper dot on tap
down equal to game controller dot on top down which is the method we just wrote and after that we just want to write flame util dot add gesture recognizer tapper and that properly binds the tap
gesture recognizer we just wrote to our game it's saved and if we click here we see that our taps are still working correctly now we're ready to create enemies in our game let's go to our
components directory and make a new file called enemy our class enemy and just like the player we're also going to have game controller so it make sure you import the file called game controller
we're also going to have health we're going to have a variable called damage a speed which will be a double a rect for the hitbox of the enemy and just like the player we're going to have a boolean
called is dead and set that equal to false now our enemy is going to have a constructor which will have this dot game controller and then it'll take in two other variables which will be x and
y and those will be doubles x and y will be the coordinates that we want to spawn the enemy at let's assign the health her enemy so we'll make that equal to three and whenever we tap an enemy we just
want to decrement the health by one and that's how we're gonna kill them we'll start damage equal to one so they do one damage the speed of the enemy will be equal to game controller
tile size times two and then we want to assign a rectangle to the enemy rect so let's go up to the rect right here hit command period and import dart UI the enemy rect will be wrecked dot from left
top with height it'll spawn at X to the left the top will be Y and then the width will set that to game controller tile size x one point two so enemies are just a little bit bigger than a tile in
the game it will also set the height to tile size x one point two it's safe and then like our player we also need the two methods so void update TT and void render which will take in canvas see
inside the render method let's assign three different colors to the enemy so what we're going to do is create color color and then we're gonna make a switch statement here so we can do switch and
then take in health and inside the switch statement we're going to have three different cases the first case will be case one and that's if the enemy is at only one HP so we'll set the color
to color 0x FF FF 7 f 7f will break case – that'll be color equals color 0x FF FF 4 C 4 C break and then case 3 color 0x ffff 4 5 0 0 break it save and then as the default case just in case
we'll set the color equal to color 0x FF and make it completely red and break it save so now what this is doing it's going to check the health of our enemy and if it's 3 2 or 1 then we will set
the color to these colors and these are all just different shades of red beneath this will make paint and we'll call it enemy color equals paint color and we'll set the color equal to color and then
all I do is draw this on the canvas so see that draw rect which will take in the enemy rect and the paint will just be enemy color hit save to test this out let's try spawning an enemy on the
screen now so back in our game controller let's make enemy enemy and remember to import the enemy component inside initialize will call enemy equals enemy
this and then we also need to write the coordinates for it so we can just say 200 200 to spawn the enemy there and then we want to render the enemy after the player
so we'll say enemy rendered see it save and let's hot restart the app and we see that the enemy appears now if we click on the enemy we see that we're clicking the position where the enemy is but
we're not doing any damage so now let's add the functionality to kill enemies inside our on top-down function for now we'll just decrement the health of our enemy no matter where we click on the
screen just so we can see the enemy change colors sold your enemy dot health – – and just hit save for now and if we click anywhere on the screen we see the enemy
change to a different shade of red click again and changed again and now if we click it changes back to red but that means the enemy's health is zero so at that point we want to actually remove
the enemy from the game let's go back to the enemy art file and let's write the code to decrement the enemy's health so or make void on top down and all we have to do is check if the enemy is not dead
exclamation point is dead then we want to decrement the health by one then we say if the health is less than or equal to zero then we're going to set is dead equal to true and then here's where
we're gonna deal with scoring in the future so for now I'll just write score [Music] the update method is what's going to allow us to move our enemy towards our
player because we want the enemy to spawn and then instantly start moving towards the center of the screen which is where a player's I so we can check if our enemy is not dead then we're going
to create something called step distance and step distance would just be speed times the time and if you remember before we set the speed of our enemy to our tile size times two next let's make
a variable that's an offset and call it two player and this is going to be the coordinate position of our player I will make it equal to game controller dot player dot player right dot Center and
then to check if our enemy is walking towards the player all have to do is do step distance is less than or equal to two player dot distance and that's the magnitude of the offset and then we need
to create another offset called step to player and we'll call it offset dot from direction to player dot direction and step distance so that's how the enemy knows
which direction to move towards and all we have to do is call enemy rect and set that equal to enemy rect dot shift step to player and the shift method moves our enemy towards the
location we specified now if we hit save hot restart the app we see that our enemy spawned but it's not moving towards the player and that's because we need to go back to our game controller
and inside our update method we need to call enemy update and pass in the time we see that the enemy starts moving away from the player it seems to be going somewhere else so we need to go back to
the enemy dark and the problem with this function is that we didn't set our two player correctly we need to subtract the enemy rect dot Center and now if we hit save I'll resort the app we
the enemy moves towards the player so if we don't subtract enemy wrecked out center we have the enemy going off somewhere in the distance but when we do subtract it the enemy goes right to the
player and ends up sitting on top of him an easy way to put some distance between the enemy and the player is by just subtracting gamecontroller tile size times 1.25 from our player distance it's
save I'll restart the app and we see that our enemy stops right next to the player and when we have more enemy spawning they're not all going to pile on top of him so they'll be sitting
around the player and it makes it easier for the user to tap each enemy let's give the enemies the ability to attack the player so we're going to write a function called void attack and we're
going to check if the player is dead by going to our game controller now player dot is dead so if the player is not dead then we want to decrease the players current health by our damage which in
this case is just 1 and now if we go to our player we don't have a way to check the player actually dies but as we just made in our enemy method we can actually do the check inside our update so let's
go to player the update will just contain if the player is not dead and the current health is less than or equal to zero then we'll set is that equal to true and then we want to reset the game
which we will do in a second but for now let's just print out the player's health in here so we'll print current health it save hot restart the app and then we have to remember the insider game
controller we also need to call player update T so that method runs we see that our health is 300 and it's not decreasing because inside the enemy door file
need to check if the enemy is in range so in this F block we're saying if the enemy is not in range of the player and move towards the player if the enemy is in range then we attack the player now
if we hit save we see that our players health is decreasing rapidly and once we hit zero it stops at zero now I think it'd be helpful if we had a health bar to our
player and we can just have the health bar sit down here so let's create a new component called health bar dart and that will be class health bar and the instance variables will be final game
controller game controller and make sure to import the game controller next we have a rectangle which will be health bar rect and the second rectangle will be remaining health rent so health bar
rect will be a red bar and then the remaining health rect will be a green bar let's render it on top of the red bar as we take more damage the remaining health rect will decrease revealing the
red health bar behind it so it's import dart UI here create health bar this docking controller and first we'll define a bar with and we'll set the bar with equal to game controller dot screen
size dot width divided by 1.75 so that's a little bit bigger than half of the width of our screen next let's set our health bar rect and we'll set that equal to rect dot from LT WH the left will be
game controller dot width divided by 2 so that horizontally centers our health bar and then we need to minus the bar width divided by 2 which is similar to why we – the enemy size and – the player
size whenever we set the position and then for the top we set that to game controller dot screen size height times 0.8 we multiply the screen size height by 0.8 and that would put it around here
because remember we're rendering zero zero up here and then the max screen size that height is down here and the max screen size not with is here so by multiplying by point eight it would put
us right around here with the health bar well let's fix this typo here fighting screen size and then the width will just be bar width and then for the height we'll set that to game controller dot
tile size times zero point five so our height of our health bar would be half the size of a tile now for the remaining health let's just copy this and change health bar to remaining health right now
let's write our render method void render canvas C and the update will be right underneath that which will take in TT renderer we'll have paint health bar color and then we'll Stan shape paint
with the color equals color zero x FF and we'll make this completely red so that's ff0000 d paint for the remaining health will be a solid green set the color equal to color 0 x FF 0 0 FF 0 0
and now we just need to draw the health bars in the canvas so C draw rect remember to draw the health bar first so health bar erect health bar a color and then we have see draw rect remaining
health rect remaining bar color let's it save and then for the update method we want to update the remaining health rect so the size of the remaining health rect decreases proportionally to
how much health the player has and then we can see an accurate depiction of how much health we have left so let's start by creating the bar with again so bar width equals game controller dot screen
size dot with and once again that's divided by 1.75 then we need to create a percent health variable so percent health will just be game controller dot player dot current health divided by
game controller dot player dot max health and that's going to return the decimal of our current health divided by our max health then we need to alter the remaining health rect equals rect dot
from left top with height and we can actually just copy this right here so exactly what we wrote before and the only thing that's different is our bar width so we just do bar width times the
percent health and it safe let's work on rendering our new health bar to the screen inside our game controller we create a health bar component health bar inside initialize you can make health
bar equals health bar this and then for rendering we want the health bar to render on top of all of our components so even if enemies go over it they're actually going to be rendered underneath
it so health bar dot render see and then update health bar update T it save etat restart then we see the enemy come towards us and it starts attacking us and we see our health bar
decrease proportionally and then once we at zero we see zero here we're now ready to create the enemy spawner so let's first go to the top of our a game controller and instead of having only
one enemy on the screen we want to have multiple enemies so we need to change enemy to a list of enemies and we'll change the variable to enemies inside initialise we can change enemy to
enemies equals an empty list of enemy and now inside the render method we're going to change enemy to enemies dot for each enemy enemy so we can do enemy dot render and pass in the canvas so what
this for each loop does is that it loops through our enemies list and then for each enemy it renders the enemy on the screen and then the same thing for our update method let's remove this under me
here and then paste this and change enemy dot render to enemy FD and pass in T it save and then down in our on top down method we can actually modify this to check if we tap on an enemy and the
way we do that is by doing enemies dot for each we get an enemy and we check if enemy the enemy wrecked sorry a new hit box that contains DEET global position so we're checking if the
enemy wrecked contains the position we just tapped and if it does then we just call the enemy dot on top down method [Music] now let's at save and if we had restart
the app we see that the enemy disappeared we can now write the function to actually spawn enemies on the screen so underneath our on top down let's make void spawn enemy and it's
going to have double x and y and x and y are going to represent the coordinate position of the enemy that we want to spawn in so let's make a switch statement and that's gonna take in
random integer and in order to use R and we need to create a variable at the very top called random R and and remember to import Dartmouth and that gives us the ability to generate a random number so
in here we're gonna have four cases case ero case 1 case 2 in case 3 the idea is that the spawn enemy function will have four different places where the enemy can spawn we have the top the right side
the bottom and the left for the top case we'll label the top we want to assign a random double to our x value so in this case our x value would be positive and we want to spawn anywhere along the
screen size dot width so we can easily do that by setting X equal to R and dot next double and then just multiplying it by the screen size dot with for Y in this case we want it to be constant
since the enemy is spawning at the top of the screen and want to be just a bit off the screen so enemies can spawn anywhere up here at any width so we just have to set our y equal to a negative
tile size times 2.5 so if you guys remember we render at 0 0 in the top left so X would be anywhere along this range and then Y it would be 2 point 5 tiles in the negative direction so up
here and now enemies would spawn up here and then move towards the player so they're coming from off the screen now for a case one that's going to be the right side and in this case we want
the x-value to be constant so we're going to set it to screen size dot width plus tile size times two point five now this is screen size over here so it goes all the way over here to the right and
then we add a tile size times two point five which brings the enemy to spawn just right outside the right side of the screen now why would just be R and X double x
screen size dot height and that can be positive because the positive value for the height is all the way down here so it can spawn anywhere along this line and then case two would be the bottom X
would be random next double time screen size dot with so same thing has our keys for the top but our Y would be screen size height plus tile size times two point five and the reason we add screen
size dot height is the same reason why we add screen size not within the last one we need to go all the way down here and then we add the tile size times two point five so now we're just right
outside the screen case three is the left side X would equal the negative tile size times two point five and Y would be the random nextdouble times the screen size dot height so the
enemy can spawn anywhere along the height and the X is negative tile size times two point five because we need to be in the negatives to spawn just right outside the screen and remember to add
break after each one of these cases right after our switch statement all we have to do is enemies dot add and pass in a new enemy with this and the coordinates XY it save and this function
would now spawn an enemy at any location outside we can actually test this right now if inside initialize we just call spawn enemy and let's Hawtree sort the app and
we get this error saying that next in was called a null so let's double check that we have the random R and here and what I forgot to do was initialize it and initialize so right after resize
let's just add R and equals random now we hit save I'll restart the app once again enemy spawns off the screen and starts attacking us
we can also test killing enemies by clicking them so we tap once we see the color changes tap again color changes again one more time and the enemy becomes fully red so let's just
implement the feature that kills the enemies and removes them off the screen inside enemy dark we check if the enemy is not dead will you decrease their health and if their health is less than
or equal to zero then we set the variable is dead to true now inside the game controller we're going to add another loop over the enemies and we're going to call it remove where we get an
enemy back and we can just say enemy dot is dead now if we hit save we see the enemy disappeared and what this is doing is that it's looping over the enemies list getting an enemy if the enemy is
dead then the enemy gets removed from the enemy list and that's why the enemy does not get rendered anymore let's create our enemy spawner now inside our Lib folder let's make a new file and
we'll call it enemy underscore spawner dart class enemy spawner it'll take in final game controller make sure to import it game controller and we'll also have a bunch of integers to
keep track of the spawn intervals so let's make final in max ball and interval set equal to 3000 final and min spawn interval set this equal to 700 final in interval change equals 3 final
in Max enemies we'll set that equal to 7 I love enter for the current interval and then an integer for the next spawn and I'll explain all of these variables as we use them in our code but for a
quick overview we have the Mac spawn interval which represents the maximum amount of time between each spawn and we set it to 3,000 milliseconds which is also 3 seconds for our min spawn
interval we set it to 700 milliseconds which is 0.7 of a second this is the least amount of time between spawns interval change represents how fast we're going to be decreasing our
interval by so as we spawn more enemies we will decrement how fast our spawn interval is so we can spawn enemies faster maximum enemies represents the amount of enemies we can have on the
screen at one time current interval is the current interval that we're spawning at and next spawn is the time we're going to spawn the next enemy so let's make our enemy spawner constructor this
dot game controller and then we'll call the function called initialize will have void initialize and in here we'll set the current interval equal to the max on interval which is 3 seconds and then
we'll set the next spawn to the date/time dot now dot milliseconds since epoch plus current interval we also should write a function that kills all the enemies so when we start the game
again we instantly kill the enemies so avoid kill all enemies will go access the game controller dot enemies which is the list of enemies dot for each enemy and then we'll set enemy dot is dead
equal to true remember to import enemy and what this is doing is it's accessing the enemy list iterating over it and we set each enemy is dead to true and then the update function
in our game controller removes all enemies that are dead so that's how we killed the enemies at the top of our initialize function let's just call the kill all enemies method now the last
method we need in our enemy spawner is update and now we'll just taking TT as usual the first variable we have to create is called int now and we'll just set that
to daytime now milliseconds since epoch next let's make a check for if game controller enemies dot length is less than the max amount of enemies we can have and the current time is greater
than or equal to the next spawn so we check if we have the maximal enemies on screen already and if we don't then we also check if the current time is greater than our next spawn which means
we can spawn a new enemy so we call a game controller dot spawn enemy which is the function we wrote before which spawns an enemy at the top right bottom or left each with a 25% chance to
decrease the interval that enemy spawn at we can do another check so we check the current interval if it's greater than the min spawn interval so if the current interval is greater than 0.7 of
a second then we want to take our current interval and subtract the interval change and we set our interval change to 3 then we also want to subtract the current interval by current
interval times 0.1 so 1/10 of our current interval dot to int and this way our enemies will spawn faster outside of this block Oh F do is update our next bond and that'll speed now plus the
current interval let's it safe and our enemy spawner is done so we cannot implement that into our game by creating the enemy spawner enemy spawner makes sure to import it
call it enemy spawner inside our initialize function enemy spawner equals enemy spawner this and we don't need to call it in render because we're not rendering the
enemy spawner anywhere on the screen we need to call it an update because that's where we're going to be dealing with the intervals so at the top of update we'll call enemy spawner dot update and
passing T now let's hit save let's restart the app and we get the error that 4-h was called a null and this is PS enemy uses null at this time which means that back in our initialize
function we have to make sure we create the enemy spawn or instance after our enemies list so we have a list to iterate over now we see that our enemy spawned here if we click three times
enemy disappears and the enemies are spawning now and we see that enemies are spawning from all the sides so it's working and right now we have three six seven
enemies and that's the max amount of enemies we wanted on the screen at one time now let's add the score dreary game so at the top let's add another variable called score and this score we're just
gonna set to zero but we can also remove these spawn enemy right here inside initialize now we have to think about when we want our score to update and if you guys remember when we wrote our on
top down inside enemy on top down we wrote score here in a comment and that's because we want to access our game controller dot score and just increment it by 1 because when an enemy dies we
want our score to update so inside here if we just print game controller dot score it save let's hot restart the game and now when I click on an enemy we should see our score increment inside
the console and we saw it flash for a second so to clean this up let's go to our player and remove the print current health and if we click on the enemies we see the score increasing it's four click
kill another one it's five so our score is working fine for resetting the game inside our player dot dart all we have to do is call it game controller dot initialize and
that's going to reset our whole game because the initialize function in here resets all of our instance variables back to their default values let's create a new component to render our
score text on the screen that component will be called score underscore text dot dart and it'll be class score text final game controller game controller when we need to use a text painter and
have an offset position make sure you import the game controller import painting and now for the constructor this game controller inside here we're going to define our painter so painter
equals text painter we'll set the text aligned to text-align:center and then we also have to set the text direction text direction would be equal to ltr' because that's left to right and since this game
is in English we read from left to right once it save on that I'm in position we can just set that to offset dot zero we also need to create the render method and that will take in a canvas for this
we can just do painter paint and we paint the canvas at the offset position the last part of our score text is the update method and we need the update method so we can constantly check the
score and update the score text accordingly so we're going to write an if statement and we're in do painter dot text and because painter dot text will not
always have a value we can do an Ola where operator and if it's null then we set it to an empty string we say is not equal to game controller dot score not to string so this is doing is it's
checking if the painter text is not equal to the current score then we want to update the score so painter text is equal to text ban text game controller dot score to string we also need to
include a style and when you say textile color colors black font size and we'll set that to 70 point oh and we need to import material for the colors now we need to do painter that layout and this
is going to get the visual position of the text so we know how wide it is and how tall it is and then we can set the position so a position will just be offset and that would be game controller
dot screen size dot width divided by two and then the same thing we do with all components in this we subtract the painter width divided by 2 let's do the same thing for the screen
size height replace with width height and instead of dividing by 2 for the height we can actually multiply by zero point two which would put the text right around here at point two of the height
inside our game controller we can make the score text score text score text it save and then initialize score text is equal to score text this save and we also need to
render it so let's render it right after the enemies so enemies will go underneath it score text dot renderer C and then the update we need to update it so score text update t let's it save
I'll restart the app and now if I kill an enemy we see that these score increases and now the score is working fine and for now I'll just comment out the enemy spawner and tatra sort the app
so let me stop spawning in order to implement the high-score feature in per game we need to go to our main dart and remember when we added the shared
preferences package in the beginning of the tutorial we need to use that now so above our game controller we're going to write shared preferences and import the shared preferences dart storage equals
oh wait shared preferences get instance so we're getting an instance of the storage and then we want to pass the storage instance into our game controller so
we'll say storage there save that inside our game controller at the very top let's create final shared preferences called storage and then our game controller constructor will take in this
dot storage it save and now we can use the storage variable in any of our components and the place we want to do it is back in our enemy dart right after we increment our
or we want to do a check to compare the current score to our high score so we do if game controller dot score is greater than the game controller dot storage dot get int for high score and so if our
high score is null which means that it's our first time playing and we never set a high score then we just want to assign zero to high score and so if our current score is greater than our high score and
we just set the game controller dot storage dot set int high score and the value is just game controller dot score and that's how we set our new high score in order to show the high score on the
screen we need to create another component but before we do that let's actually create a new file in our Lib directory called state dart state dart is going to be an enum state and it's
going to have two different states menu and plane so our game will have two states one where we're in the main menu and we're not playing the game and once we tap then we change the state to
playing and that's when our enemies will spawn inside the game controller we need to create state state and make sure to import the enum inside the initialize function we just need to set state equal
to state dot menu because when we initialize the game we want the game to initially be in the main menu a state you know Millau us to check whether or not to render certain components on the
screen or update certain components so inside our render method right after player dot render will check the state so state equals equals state menu so when the state is menu what do we
want to render we want to render the start button and the high score text otherwise we want to render all of this and we can just put else here and I'll render this if the state is not a menu
but if you guys ever want to add more states then you just do else–if state equals state playing and just do a check for it and that would work the same way inside our out date method we do the
same check so state state menu else if state playing so we're playing the game then we want all this in there and don't forget the enemy spawner inside the menu state we just call start button not
update and high-score text dot update and Freyr it'd be a render see render see so let's not restart the app and as you can see we're in the menu state right
now because the enemies are not spawning we have no health bar and there's no score the way we want to be able to start the game though is by just clicking the screen so inside our void
on tap down we can remove the print right here and we'll do another check if state is equal to state menu then we want to set the state equal to state a plane so the game starts
otherwise if the state is equal to state dog playing then we wanted to do the check to see if we're clicking on enemies and if we click on an enemy then we want to damage them now if I click
the screen we see that the game starts and we have the score up here and the bar down here and if I die then the game goes back to the main menu so we're almost done with our game all the
functionality is working we just need to create the high score text here and the start button so inside components let's create a file called high score text dart and if we go to our score text
let's just copy this whole entire file and paste it into high score text and change score to high score text and then the bulk of our change will be inside our update the first thing that's
different is that we need to make an integer variable called high score and that will just be game controller dot storage dot get in high score and if there's no integer which means that we
never actually played the game so there's no integer to save then we just set it to zero after that we don't need to check anymore so we can remove that if statement
the painter text instead of doing game controller dot score dot to string we can change this to high score : and then use the dollar sign and put high score [Music]
let's change the font size to 40 and that's pretty much it for our high score text game controller we'll add the high score text right here high score text
I score text remember to initialize it and it takes this inside our render method let's uncomment the high score text render and our update let's uncomment ice core text I'll update it
save let's hot restart the app and now we see our high score text appeared if we click to start and let's kill one enemy and then once we die we can see the high score update so I just looked
at why our high score was not beating and it's because I forgot to add parentheses around the game controller that storage that get in inside our enemy door so make sure you guys add the
parentheses here here's what was happening was that it was comparing to game controller that sorts I get in and since there was no value there it would not go into this block so again if you
guys have the same error to make sure to add the parentheses right around this whole statement and then your high score will save the last thing we have to implement is the start button so inside
components we can make something called start button dart class start button final a game controller game controller text painter painter offset position and I guess this is more of a start text
than a button because we're displaying text down here so we can just change it to start text change this to start underscore text import material dart and then we'll make the constructor start
text this stock game controller painter will be equal to text painter text align text align dot center text direction will be left to right because this game is in English and position will be
offset dot zero and since we're basically writing the same thing as our high score text we can just go into here copy the render and update functions go back to our start text and its pop it in
here let's change this update method here so we don't need the in high score etext will just be start and will make the font size 50 for the position of the star text will make it point 7 so it'll
be right around here and then back in our game controller we can add start text start text and the usual now so start text equals start text this inside our render method will say start text
dot render it save and then the update will be start text dot update let's hit save I'll restart the app so you stop beating these errors and now if we click start our game starts we can tap enemies
three times to kill them and we see our score increasing and I'll keep going for a little bit just so we can see how many enemies will spawn and now we see that the enemies are
spawning a lot faster so of course this games a lot easier on the phone because we can tap multiple enemies at the same time on the computer it's a bit hard but yeah that's the game
congratulations on building your first floor game let me know in the comments below if you enjoyed this video and want me to build more games with flutter remember to leave a like subscribe share
this video and to start up a story and get home thanks for watching and I'll see you in the next one [Music]
Preparing for coding interviews? https://algoexpert.io/marcus
Use promo code “marcus” for a discount!
Welcome to my Flutter Apps from Scratch tutorial series where you will learn Flutter and create amazing apps using Dart. In this video, we learn how to build a basic Flutter game!
» Timestamps
00:00:40 Setup
00:03:57 Game Controller
00:07:26 Creating The Player
00:12:54 Importance of Rendering Order
00:13:38 Tap Functionality
00:14:56 Enemy Class
00:19:17 Spawning A Single Enemy
00:20:43 Enemy Health, Movement, and Attack
00:26:20 Health Bar
00:32:46 Multiple Enemies
00:34:47 Spawn Enemies Off Screen
00:39:47 Kill Enemies
00:40:51 Enemy Spawner
00:47:39 Scoring and Score Text
00:54:25 Shared Preferences (Save Highscore)
00:56:30 Game State
00:59:56 Highscore Text
01:02:14 Saving Highscore Bug Fix
01:02:47 Start Text
» Remember to like, subscribe, share this video, and star the repo on Github 🙂
https://www.youtube.com/MarcusNg?sub_confirmation=1
» Resources
Source Code: https://github.com/MarcusNg/flutter_swarm_game
Flame: https://pub.dev/packages/flame
Shared Preferences: https://pub.dev/packages/shared_preferences
» UDEMY: The Complete Flutter UI Masterclass Coupon
https://www.udemy.com/course/the-complete-flutter-ui-masterclass/?couponCode=B5D3C1BA98B898FC6605
» UDEMY: Flutter and SQFLite Coupon
https://www.udemy.com/course/flutter-and-sqflite/?couponCode=749216595DBD298E3599
» Socials
GitHub: https://github.com/MarcusNg
Twitter: https://twitter.com/MarcusLNg
» My Gear*
Logitech Master MX 2S Wireless Mouse: https://amzn.to/35BZoYy
AirPods 2: https://amzn.to/2MdpJVb
Apple Magic Keyboard: https://amzn.to/2nCXqpu
Blue Yeti USB Microphone: https://amzn.to/32cs6wM
JBL Charge 3 Bluetooth Speaker: https://amzn.to/2IZuxeT
*Affiliate links
» Outro Music
Harmony – Joakim Karud (https://www.youtube.com/channel/UCVBUm9ccjHy0utvlChyrPHA)
#Flutter #FlutterGame #GameDev