Monster shadow

VIDEO

Create 2048 in Unity - Learn how to make a grid game [Part 2 of 2]

Description:
This is part 2 of the 2048 series. If you haven't watch it, go check it out now and come back: https://youtu.be/TeurfjuEIgA In this part we learn how to shift and merge our blocks. Now it's getting exciting!! Scripts and Assets: https://bit.ly/38CA07z
ADD A COMMENT

Deviken Games
very good tutorial sir,i hope you will be able to record ore videos about making games examples like this one,keep posting sir plz
Yonatan Lischinsky
Hi, very good tutorial! One small issue with the losing condition: even when there is 0 free nodes on the board, you need to check if there's a legal shift(must be a merge), so only cheking for the last free node is not enough for the losing condition. You can think of a simple example: The board is full with 2's - it is still not game over because the user can shift.
Tarodev
Ahhh, is that how the real game works? To be fair I actually only played 2 games of it before deciding to build it, heh. Good to know, thanks! ❤️
Jason Moreland
Is it possible for you to put the code somewhere, I still have an issue with blocks merging OK but then at around a value of 32, lower value blocks being able to move fully left or right onto occupied nodes but have been unable to find where I have made the mistake by jumping around the video so it would be helpful if I could compare it directly with what you have. Seems like the set occupied is being missed somehow, regards.
Jason Moreland
Cheers, looks like i was running the sequence incorrectly. sequence.OnComplete(() => { foreach (var block in OrderedBlocks.Where(b=>b.MergingBlock != null)) sequence.OnComplete(() => { var mergeBlocks = orderedBlocks.Where(b => b.MergingBlock != null).ToList(); foreach (var block in mergeBlocks) { looks like not caching it caused it to recalculate incorrct values each run.
Jason Moreland
@Tarodev great ill check it thanks,
Tarodev
@Jason Moreland Check the description :)
Jason Moreland
@Tarodev cheers, its been driving me nuts. Love these tutorials and your lamda one.
Tarodev
Oh man, I thought I put the code in the description! Yes I'll do it the moment I get home.
Robert Banister
Can't stand the pounding of the keyboard, coming out in your microphone. Geezz
Tarodev
My microphone was set to omni-directional for this video without me realizing 😢
Егор Франц
Very nice! Thank You!
Mustafa YILDIZ
Hi thanks for the tutorials, i have a video idea. Can you show 2d enemy behaviors for sidescroller games like ranged patrolling archer enemy, patrolling melee enemy, flying ones with certain range follow player and shoot projectile etc.
Tarodev
This is a great idea
Uv Hertzog
Very cool
SEE ALL COMMENTS


Transcript:

this is part two of the 2048 series if you didn't watch part one go watch that
now and then come back here to finish the game
let's get into it okay so last time i
helped you create this grid here which spawns our blocks onto the nodes so now
we need to actually shift our blocks uh depending on the player input so
we have just finished our spawn block state and we need to go to our waiting
input state so let's say change state
weighting input and then
let's hook into our update function so basically if if it's not weighting input
we don't want to do any things so we'll say if state does not equal
waiting input let's return don't do anything
but if it is let's say if input get k down
and let's say the left arrow now we need a function here to shift all
the blocks so let's create a new function void
shift and this will send in a direction
like that so then we'll say here shift and this will be left as that's the key
that we're pressing um
so now let's actually think about this because this is where it gets a little
bit uh confusing so
let's make it let's just let me get a good example here
come on come on okay perfect so
we're shifting left here so if we were to calculate this block first
we would say all right is there a space available next to me yes
okay is there another space available next to me here
no because there's this four block here so this one would actually shift to here
then we would go to calculate this one which would shift to here but that's not
what we want what we would actually want is them both to shift across so this one
would go to here and then this two would now shift across and take this spot here
so we depending on the way that we're shifting we need to order our blocks so
that they calculate in the correct order so how do we do that
okay so if we were shifting left here we know that we need to
calculate from left to right so we would order our x to go from zero up
and also in the same way if we were going to shift uh
if we were going to shift down we would also want to order our y to be
from zero upwards because we would want to do from bottom to top wouldn't we
so let's just do that uh
instance first so let's say ordered blocks
equals uh blocks dot order by now first we're going to order it by the
blocks x position okay because we want to go
from 0 upwards and it would be very handy if in the
same way that we've got this helper function here on the node we've also got
it on the block so now we can just say let's order it by
the blocks x position
and we also want to do the same thing but with the y so we'll say block
um whoops no so first we want to order by
then we want to do then buy so first we'll organize by the x then we'll
organize by the y so b
and then the b position y so now this ordered blocks list has
blocks ascending their x and y but
what if it's the other way what if we're shifting
not to the left but we're shifting to the right or what if we're shifting not
uh what if we're shifting knots
down but we're shifting up so if we're shifting up or right we need to change
our behavior so if
direction equals uh
right or direction equals up
let's actually full on order uh reverse our list so this will be to list
and this will be ordered box reverse okay so now
our ordered blocks list with just those two lines will already
be in the correct order for us to calculate all of our blocks
hope that makes sense okay now this is
this is really getting into the meat and potatoes of it
okay now that we've got our ordered blocks we will loop through them
[Music] lock
and we will so i was thinking about how to do this
actually in the shower last night how to best to do it so
i'm going to do it in a do while loop and the reason i'm using a do while loop
instead of a while loop is because i always need to do this at least once for
every block so the difference between do while is it
will always execute once and then basically we'll just act like a normal
while loop so basically what we're going to do
is we are going to start with our current position so var and i'm going to
call it next and this will this will all make sense in a moment and i'm going to
call this block and i'm going to take its node which i
haven't put on yet so this block just like the node holds the occupied
block the block itself is also going to hold a
reference to the node that it's on so
node node
and we actually also need a function here so that when we actually set down
our block it sets its node so set block
and this will take whoops it will be a void set block
this will take in a node so i'm going to say
first if we're moving this block and i'm already on a node i need to release my
current nodes hold i don't know so i'm going to say but this but we may not
actually have a node because it might be the first time we're actually setting
the block down so no node is set so we have to actually if check this if
node does not equal null we'll say node and
we'll take its occupied block and we'll say
null because it used to be me now it's not because i've moved
we'll set our node equal
to the new node that we've just sent in and we'll also say the nodes occupied
block now this new block that i've just um occupied i'm going to say i am now
the new block that is part of that occupied block
so set block so now before we continue here when we're
spawning these blocks here we're initializing them
but we also need to set the block to the node
okay all right so now down here as we've got
our node reference now so basically what i'm doing is i'm saying
the first place that we're going to check is
ourself okay so that that's always going to return true there is a spot because
we know we we're already in it so here the first
the first thing that we're going to do is we're going to say
block set block
and we're going to send in um next
okay so we're immediately we're already saying all right the next block that we
need to check is our own okay so immediately into this do loop we're
saying yep block set ourself again okay we're literally unsetting it and setting
it but now we need to do something we need
to set next to another block the next one down the
row so that on the next iteration of the
while loop this next will actually be the next block in line right
okay so what we need to do now is we need to find a possible
node and we need to
take our current node which is next okay so that could be the node that the block
was originally on or it could be the next the next block next to it depending
on how many iterations of this while loop there's been
we need to get uh so let's make a little function here actually
this will be node get
node at position and this will just take in a vector 2
pause and now the beauty of using just a
standard list as opposed to a 2d array here with an x and a wire is we don't
need to do any special checks to see if it's out of bounds of the array we can
just say i'll return nodes
first or default node
position equals position that we just sent in
like as simple as that if we were using a 2d array here we'd have to say
is x less than 0 is y less than 0 is x more
than the width is y more than the width we would have to do all those checks
before we can even even uh ask the array or else we'll we'll throw
an error and out of bounds error so that's the beauty of just using a list
not as performant but far easier to write and understand
so possible node get node at position and we're going to
be sending in our next
dot position okay so the one that we're already on
plus direction so our shift direction
now this could return null if it's out of bounds so this might be like minus
one uh so we need to check here we need to
say if possible node does not equal null
so now we know that a node is available we know a node is available
sorry not available is present so now we just need to check is that
node available for us to actually place on so we can say if
possible node dot occupied block equals null
awesome that's what we want let's say next
our next block to check equals possible node
do you say so now let's just go through this we're saying
we're starting with our current node and we're just setting it immediately
but then afterwards we're saying all right is there another node next to me
if there is and it's available let's set this next variable to this new block
that i've just checked is available okay so then in this while loop will say
this will be the condition to see if we should go back up to the top and set
this new block as our as our furthest block so we'll say this
condition will be if next
does not equal uh
block dot node
yes sorry our next as long as
yeah so if next is different to our currently set node then we know that we
have actually set something here and that we can continue this loop
does that make sense and just for um demonstration's sake
let's just transfer let's just change the transform direction right now like
as we're here so we'll say uh block.transform.position
equals so we've here just discovered our furthest block that we're actually can
move to we'll say block transform position and we'll send it to now the
block node
dot position because just remember through this do loop
we are setting the new nodes each time each
time we move we're setting it that doesn't mean that we're actually moving
to it okay so we've got to manually do that here
so let's try this out let's go back to unity
and if we press left here perfect so it moved to here
and then it reset the do loop and then it moved to here reset the do loop and
then it tried this one it tried this but it said
it returned no yes there is a possible node here
but is that is it occupied and yes it is so it did not set the next
node all right which means this next is the same as the block's current node so
then it came down to here is next different to this blocks node
if it if it isn't then we haven't found a new node to go to so just exit the
block and then move the block there okay does that make sense let's just try
it one more [Music]
time perfect working just as expected
now uh just teleporting the block there isn't too fun and it also doesn't uh
give as much visual debug like if you can imagine
when there's all these blocks that are going to be on the screen and we're
trying to work out all these bugs you know why is this happening why is this
happening just pressing the buttons and then the blocks just teleporting all
over the place it's not going to tell as much
it also doesn't look that good so instead of showing you how to manually
load objects which is not actually how i would do it anyway i'm going to show you
a package which will do it for you and to be honest it's the the best unity
package uh that has ever been released in my opinion i use it in every single
project so it's called uh dot twin and basically it will just handle all
of your transform movements okay and you can add
different um ease types to it and it's just it's just
super super easy to use so go there this is free by the way uh just
grab it from the package manager if it's not showing up here go to the unit asset
store just click check click import into unity and you'll
have it so i'm just going to import when it
imports you'll get this little menu here and the good thing about dot twin is
they know that you're not going to use every single component of it so you can
actually turn off
everything here which we don't need any of this
and apply and it makes the binary
the the compiled version far lighter on your on your end result so now that
that's done if you just go to your scripting editor
re reload as a whole load of stuff has just been injected
now instead of us saying this here right we can say now this is this is the
magic we'll say block.transform and
dot twain has given us a whole load of functionality here we can now say do
move okay do move and we want to say the
first the first parameter is
the end value where do you want to move this and we want to move it to the
position of the node and then we want to say how long do you
want this transition to take now i
want all my blocks regardless of their travel distance even if they're just
moving one or if they're moving all the way four i want it all to take the exact
amount of time it will i'm sure it will give it a nice snappy feel
so serialize filled float
travel time and i'm going to default this to
something like two that might be a little bit too fast but we'll see
so now i'm just going to send in that travel time just like that
so now you'll see with just just that one
little line there how much improvement this will make
like that and while we're at it let's
um duplicate this line here and we'll add writes let's not write
alts right and then this will be up array
up down array
down so now we should be able to move all over the joint
down beautiful look at that
[Music] well yeah we can do that because uh
[Music] we're not actually stopping player input
we'll get to that okay so actually we can get to that
right now we can say as soon as we've triggered
this shift we'll say change state um moving
so now we won't be able to actually add additional input
okay now we need to worry about merging so
um okay so now in here we're saying
if the if the block is not occupied cool we can move to that location
but what if the block is occupied but it is the correct block that we need
so let's change this let's say if possible node
dot occupy block does not equal null and
possible node dot occupied block dot value
is equal to r block value if so
[Music] we need to
say that this block can merge into this new block
so uh let's add a
public variable here on our block and this will be called block
and let's just call this merging block so we're saying
if this is set we can check this and then everyone can
all other blocks can know this block is in the process of merging
okay we don't we don't care what block this is
they just need to go if it's if it's not null then we know this block is merging
in this transition so
here we can so here we've said the block is the the block on the next space is
not null and it's the same type as me so what i'm going to do is i'm going to
say block
merging block equals the occupied block and then
we also want to make sure that if there's other blocks behind me
they know that they can now take my new spot also i need to
let the other blocks who might be behind me
know that my spot is now available even though that i'm not setting setting
block okay so a few things are going to have
to happen here so let's let's create a function on the block
public void merge block
and this will take in a new block and this will be the uh the block to
merge with so then in here we can say our merging
block equals
the block to merge width [Music]
we want to allow other blocks to use our spot so we will say node dot
occupied block equals null [Music]
i know that there should be something else here
okay so we're saying that yes other nodes can go into
our spot we're saying that
i'm merging already so i
so nothing else is allowed to merge with me
because i'm already merging but what about the block that we're
merging with so the block that we're merging with
also needs to be telling other blocks that they can't merge uh because they're
already merging so yeah so picture this ah perfect
picture this so say there's a two here a two here and a two here okay
and we push them all to the left so then the first twos will start
merging and we only want two blocks at a time to
merge we don't want all three blocks to merge so
these two will go into this block this one
will unlock its space and say yep i'm merging with this one
so then this one will come along and it will check this block and it will be
like nope i can't merge into you but then it's going to come over to this
original block here and there's nothing saying that this
block can't merge because it's a two so i'm just going to merge into it so we
need a way to make the original block also say that they're merging
so this here is for
the block that is merging but we also need
a variable here for the original block
so this will be merging so then we can say
block to merge with dot
merging equals true and that that will protect both blocks
um set the base block as merging so
it does not get used twice
or three times or four times depending on how how big your board is
so uh let's also make a little helper
function so public bull
can merge and this will take in a value
and will return first up is say so this function will be
called by another block so it's going to detect
the block it's going to send in its value so we're
going to say first if value equals value so these two values match
and [Music]
we're not merging and
uh merge merging block equals null [Music]
so we need to check all of those things and if all of those things
uh return true then we can say
yes we can actually merge this block so instead of doing this check here
manually we can just say uh is the block does it have an occupied
block and it's not null yes okay well let's say possible node occupied block
can merge and we're going to send in r block
value so that will return can our new block
merge with this current block if so let's say
block merge merge block and we'll send in the
possible block the occupied block and we only want to
run one or the other so let's say alph elsa here
i know this may sound a little bit convoluted but it's a work in progress
we'll see so here we know the note is present
if it's possible to merge [Music]
set merge otherwise
can we move to this spot and then if none of those hit
we're basically not doing anything we're just
we're just saying you know uh if not if none of those hit
we're saying none here
um and do while loop because we haven't found a new block to do anything with
so let's see if i haven't broken anything yet
and it looks like i have ah that's because we're setting our
chain state to moving and then we're not actually continuing
the game so to do that
this is an asynchronous function right like we're we're calling it and then
curry can continue down here but while that's happening this is still
moving to its to its destination over the travel time so we need a way a way
to wait for this um and to be honest we don't really want
to do it here anyway so let's now now that we have
uh gone through our ordered blocks and we've set our new block positions
whether it's by setting it to the actual place or saying that we're merging
we can now go over this 4h
in ordered blocks we're going over the list again
and we're going to say far like where do we need to move now so
it's either going to be to our new node or if we're merging as a block it's
going to be to the base block isn't it so
uh move point equals and we're going to check block are we merging
if so our move point will be the block
dot merging block dot position
otherwise we're going to be moving to the block
dot the node that we've set dot position so now we have
our end point now a beautiful thing about dot twin here
is we can we can queue up a whole load of
different animations and then get a call back when it's finished so look here we
can we can actually say on the end of this
[Music] dot on complete
right and then we can actually run a function once this completes so we can
run this logic when this when this block actually finishes transitioning but we
actually need to do this for a whole load of blocks don't we so we need to
know that they're all finished so we can do this we can say var sequence
equals dot twin
dot sequence and we can take this little
move script here and we can just say sequence dot insert
and we're going to be running these all at
the same time we don't we don't want to sequentially run them we want them all
to run asynchronously so we're going to say
we're going to insert this at index 0. we're going to just chuck in that
that twin that that we just had up here right
and now this sequence is holding all the blocks
uh travel places
travel travel animations so we don't want to hard code that we want to
actually move it to the move point and now instead of directly saying on
here on complete we can now say sequence sequence on complete
[Music] please run this code once once they all
once they've all finished their their animation we don't care about the ones
that we're just moving to a new spot but we do care about the ones that were
merging we need to do something about that so 4h
uh ordered blocks where
block uh
merging block does not equal model
yes so we're going to loop through all the
ones that are merging into a new block and to merge we need to do a few things
so let's make a function merge blocks and we'll say block
base block and then block
uh merging block
[Music] so the first thing we need to do here
and let's just call this match blocks block
and then block merging block
pretty easy now we already have a function called spawn blocks
spawn blocks so that's that's being called with a number with an amount and
then we spawn all the blocks uh using that
but that's not really what we want we want another function called spawn block
we just want an individual place to do this and we want to send in
a node and probably also a value
[Music] so if we just grab this code here
so then the node is being used and then the value
straight up and we'll just cut that out and say value
and now we can call this spawn block send in our node and then send in that
that value string again so that's now using this
and now that we've got this function here we can specifically spawn our new
block so spawn block
uh base block it'll be on the base block
node and then the value
will be the base block dot value times two
yes so now we're spawning this new block then
we need to destroy
the two merging blocks so let's make another function
remove block block
and then we'll say first we need to remove it from the list
so blocks dot remove and then remove block yeah
finish it for me and then we need to actually destroy the
object so let's destroy block
game object cool so now we need to just destroy
these two blocks so remove block face block
and then remove block merging block
[Music] cool so we're going we're moving them
when they're finished we're going to go through the ones that actually merged
and we're going to say merge block which is going to spawn the new block of the
new value and remove our previous blocks that just
merged yes
cool and we don't need to reset
these two variables because these blocks are
actually getting destroyed so then once we merge those blocks now
let's say change states spawn blocks
spawning blocks and that will now go to spawn the blocks but now the round
is at least over zero so now it's only going to spawn the one block then it's
going to go to our waiting input so let's see if that works
it's going to be a miracle so up down
oh object reference nut let's check what
that is make point
block dot block.merging if block's merging
do that otherwise
so we this merging block
is not the orchestrator we need to say if block
merging block does not equal null then we'll use the merging block
position otherwise we'll use just our position
yes let's try that
up down that merge but that didn't look right
yes that is not right at all um
block merging block dot position aha so here we're
saying the move point is going to be the merging block position
but that's not correct we need to set it to the new node
of this merging block because if you remember we're setting the nodes but
we're not actually moving to them and then we're just trying to say here let's
just set it to the current position of the merging block which is incorrect so
we need to set it to the node the new node of this block's position
yes so so as a pointer like when you're
going through this you should always set it to the position of the node and not
the block because the node may be out of sync with the block
good rule of thumb space that is working
pretty nicely don't see any bugs until right there so
what happened there what happened
so we've got a four and an eight there what did that mean
what did that mean okay
so we're we're merging in the wrong place
so merge blocks block merging block
spawn block base block
node aha
okay so we're we're i'm so i've made this function here merge block and
saying that the first one is the base block but then here i'm actually sending
in the block as the base block which is incorrect
it's the merging block which is the base block
and then the block is the one merging into the merging block
okay let's try that
[Music] was that right
yes i think it was right [Music]
oh yes nice
i think that's working [Music]
i don't know if i'm good or bad at this game
i think i'm probably [Music]
not very good [Music]
i'm sure you guys don't want to watch me play this the whole time um plus i was
probably about to lose cause i'm trash so
uh we need a win condition don't we so
we can actually to make this dynamic we can say serialized field private inch
win condition and let's default this to 2048 as that
is the name of the game but
you can change this to any uh duplicate of 2
below 248 and we can actually make that the win condition so
you could possibly create a game where the first few levels is only like 32 you
know as a little practice so where do we
actually look for a win condition here
probably in spawn blocks right so if we've spawned a block
here we can actually check lose so instead of just putting this comment
here we can actually say change state lose
as we know that they have just spawned in a new block
in the very last square which makes moving impossible and then
down here and change states we can now say
blocks is there any
block with a value
of the win condition if there is let's send in um
game stage win
otherwise let's go back to waiting for player
input what are you doing oh that is incorrect
so is there is there any any blocks
with the value of when condition win otherwise waiting input so obviously
you have to set your win condition correctly if you set it to a number that
is not actually available they'll never be able to win so
i don't know if this would be better as uh like an enum
you'd have an enum for each number or or what but right now i've just got as
an int cool so now here
we can let's make some uh two more references private
game object win screen and the lose screen
and then in our win we'll say whoops screen set active true
golly gosh my typing then
uh loose screen blue screen set active tray
and then in the game let's create a canvas and this would
just be text on a screen and this will be the wind screen oh my golly gosh
when screen and then lose the screen
let's make these both centered in the
screen and they're tiny
so then this wind will be the winner
this one will be loser [Music]
and then let's disable them by default
and then in game manager slap in our windscreen where are you windscreen
lose screen and let's set this win condition to 32 just to see if this
works if i can get to 32
let's see let's see i think i'm pretty trash of this to be
honest [Music]
well i'm close i'm so close
[Music] 32 winner
there you go that's the game that's uh i think i i
couldn't see any bugs not too much code
not so clean but could probably be uh refined quite a lot but uh that's about
it so there it is i hope you enjoyed the
video i know it was pretty lengthy leave a like if you stayed this far and
let me know in the comments what you thought
tara dave out