Placeholder Image

Subtitles section Play video

  • [MUSIC PLAYING]

  • SPEAKER 1: Hello.

  • Welcome to lecture three of GD50.

  • Today, we're going be talking about Match 3, as shown by the little cubes

  • here on the slide.

  • Match 3 originated a little earlier than 2001,

  • but the first big game that came out that was

  • a sort of genre staple of Match 3 was Bejeweled, shown here on the screen.

  • This is a more modern incarnation of Bejeweled,

  • but it came out originally in 2001.

  • It was actually a web browser game, and the formula is very simple.

  • The premise is you have a grid of different colored or shaped items,

  • usually pretty small, like eight by eight, or so.

  • And your goal is just to simply, like the name says, match three or more

  • of them in a row.

  • If you do, you get a certain number of points.

  • Matching usually more than three gives you more points, or a bonus.

  • And whenever you match three, the blocks will disappear from the grid,

  • and they'll be replaced by more blocks.

  • And the ones that you made holes for, the blocks above them

  • will come down via gravity.

  • This is a more modern incarnation of the formula.

  • This is Candy Crush, which I think most people know.

  • It was a very big hit on mobile devices, and otherwise around 2013, 2012,

  • and that's probably the most recent big Match 3 style game that's come out,

  • but there are a lot of other takes on it--

  • different versions that try to add new features, and stuff.

  • This is the game that we'll be putting together today, and I'll show you how,

  • and we'll be covering a few other things as well.

  • So the topics today, we'll be covering, first

  • of all, a fundamental concept in dynamic languages, a lot of dynamic languages,

  • and also Lua.

  • It's called anonymous functions, which are functions

  • that are first class, meaning that they operate as data types,

  • and so we can do some fancy stuff with those.

  • Tweening, which means just taking one thing,

  • and interpolating its value between two values from 1,

  • to a destination value over time, which is a very important thing in games.

  • You can do things like move objects.

  • We can also tween their opacity.

  • Just sort of asynchronous behavior, and asynchronous variable manipulation.

  • Timers, very important.

  • We can time something to happen at certain intervals,

  • or after a certain length of time has passed

  • to get us past the idea of storing different timed variables,

  • or different counters, and break away, and keep timer objects that

  • will take care of this for us.

  • We'll see how we do that with a specific library.

  • And then we'll get to the actual details of Match 3, and how to solve matches,

  • and how to account for that.

  • Fill in the grid, account for when we actually solve a match,

  • and repopulate it once we've done so.

  • We'll talk about how to do this procedurally.

  • It's very simple compared to, I think, Breakout's more procedural layout

  • system, but it's still randomisation, and we'll talk about that.

  • And then last, if we have time, we'll talk about sprite art,

  • and palettes, which is a big fundamental thing when you're doing 2D game

  • development, and something that this, and Breakout's sprite sheet

  • took advantage of was the idea of using, on purpose, a restricted set of colors,

  • a palette for creating your 2D art, and there

  • are a lot of really cool, and impressive things we can do with that.

  • But first, I'd like to actually show what we'll be running today in class.

  • So I'm going to come here into my directory.

  • Make sure I'm in the right place, which I am.

  • So this is part of the distribution code, which is online.

  • So there's a Match 3 directory.

  • And would anybody like to come demo it in class?

  • All right, [? Tony. ?] Come on up.

  • All right, so whenever you're ready, go ahead, and just hit Return here.

  • [GAME MUSIC PLAYING]

  • All right, and so this is my implementation of Match 3.

  • It uses a different set of tiles.

  • We have things that are moving over time.

  • It's arrow key based.

  • So if you press Enter on any tile, you can flip it with another tile.

  • It doesn't have to be a match, in this case.

  • So you can-- yeah.

  • Yeah, you kind of got an unlucky board.

  • There, at the very bottom, I see there's a few that--

  • some brown ones you can match together.

  • So once you match them together, the tiles come down to repopulate.

  • You get new tiles up top.

  • And so notice, we have a timer on the left as well.

  • It's something that's counting down.

  • We'll see how this is actually done with the library we'll be using, as opposed

  • to managing a counter variable keeping track of it over time.

  • A lot of games will actually implement it so you have to-- you can only,

  • and this will be part of the assignment, actually,

  • where you can only move a tile if it creates a match.

  • In this case, there's a--

  • and we can see the timer counting down, and then once you-- yeah,

  • if you don't get past the goal, there's a game over.

  • But thanks, [? Tony. ?] I appreciate it.

  • AUDIENCE: No problem.

  • SPEAKER 1: So that's the game in a nutshell.

  • And another thing I want to point out to is

  • the transition, the white transitions, and then the level text.

  • Those are all done with timers that we'll be using,

  • and tweens, which we'll be covering in class here

  • as some of our early examples.

  • But there's a lot of stuff that we haven't touched on, but also

  • a lot that we have.

  • It uses sprites, and a sprite sheet, and we've done that thing before.

  • We chop up a sprite sheet, and then take out the whatever individual quads

  • you need, and draw them to the screen.

  • Here's what our goal is, which is we have a title screen with Match 3,

  • start, and quit game in this case.

  • A little bit simpler.

  • No high scores this time just because we've already covered that,

  • but we also have a level screen.

  • It tells us what level we're on before we can actually play,

  • and there will be a transition box with text in it.

  • It'll come down, stop, and then come down again.

  • So almost like chain behavior, which we'll see how we implement that too.

  • And then lastly, on the bottom there is our main game screen,

  • where we have a level, a score, and then a goal.

  • If you get the goal amount of points before the timer runs out,

  • then you go to level 2, and level 3, and level 4,

  • and the score increases by a multiplied factor each time.

  • So the first thing I'd like to start talking about today

  • is how we actually get timer behavior using something a little bit more

  • than just keeping track of some variable that we set to 0,

  • and then adding dt to it every update.

  • There's a better way to do that, but first, why don't we

  • go ahead, and look at timer0.

  • And so what I'm going to do is go into the timer0 directory.

  • I'm going run it, and we can see here, in the middle of the screen,

  • just a very simple--

  • just a label that just says timer, and then x seconds,

  • where obviously the x is incrementing over time every second.

  • So a crude way, what would be an easy way to implement this?

  • AUDIENCE: Do you like [INAUDIBLE] random in Flappy Bird [INAUDIBLE] randomizer

  • [INAUDIBLE] if you just keep track of your delta prime

  • and add it to some variable outside [INAUDIBLE] you do something else?

  • You could even just display the variable [INAUDIBLE]..

  • SPEAKER 1: Yes.

  • So the response was keep some variable that you modify with dt in update,

  • or display the variable.

  • Yeah, that's definitely a way.

  • Did you have--

  • AUDIENCE: Yeah, I was just going to say, keep a float variable,

  • and constantly add a dt to it, and display it,

  • but display the truncated version.

  • SPEAKER 1: Yeah.

  • So keep a float variable, but just truncate the delta time off of it.

  • You could do that, definitely.

  • We'll take a look here as to actually how I did do it.

  • It's very similar to that.

  • This is the wrong directory, though.

  • So it's in timer0 in main.

  • So we do have a variable.

  • So the current second here, which we are going to keep track of 0, 1, 2.

  • Lua doesn't really have the notion of truncate a float

  • because when you take a number that's floating point,

  • and you make it into a string, you actually

  • have to do string substitution on it where

  • you use a function called g sub to take off the last part manually

  • because it doesn't really differentiate between ints and floats.

  • It just has a number data type.

  • But we can do this by just keeping track of

  • whether or not we've passed a certain length of time

  • because we know dt is given to us in seconds.

  • We can just add to our variable, and then every time we've

  • gone over 1, because it gives us--

  • it gives you usually like .013, whatever 1/60 or approximately 1/60 of a second

  • is.

  • Once our timer-- we're going to keep a timer variable-- equals 1,

  • we'll just increment current second by 1,

  • and then we'll set that timer back to 0, and then

  • we'll just repeat over, and over again.

  • We'll actually use modulus so in case we go slightly over 1 second,

  • we can account for that.

  • We do that here.

  • So second timer gets second timer plus delta time,

  • and then if it's greater than 1.

  • So if a full second has elapsed, just increment current second,

  • and then modulo a second timer by 1.

  • And Lua is a little different in that most languages only let

  • you modulo something if it's an integer, but since there is no differentiation,

  • you can actually modulo floats, and you'll

  • get the floating point value leftover.

  • And so that's the basic way of actually doing that,

  • but there's a couple of things wrong with it.

  • So does anybody want to suggest what is potentially bad or unscalable

  • about this kind of approach?

  • Well, I'll show you timer1 so we can maybe

  • get a sense of how this could kind of get out of hand pretty quickly.

  • So let's say-- first, I'll run timer1.

  • So let's go into timer1 here, and notice now we have five labels.

  • They're running at different intervals.

  • The first timer, it's incrementing every 1 second,

  • the second timer is incrementing every 4 seconds,

  • the third one is incrementing every 4 seconds, and then so on, 3, and then 2.

  • So if we wanted to do the same approach that we just did,

  • this is what we would do.

  • We have five variables, five timers.

  • Because we want to keep track of whether or not

  • something's gone over more than just one second,

  • it's not super easy to just put this all in a table,

  • and iterate over it, and use your iteration logic to do that.

  • We actually, because they're in some sort of random, who knows what order,

  • timer2 takes 2 seconds, OK, timer3 takes 4 seconds, timer4 takes 3, and then 2,

  • you have to unmanageably keep all of this in separate variables.

  • [? Yes, Tony ?]

  • AUDIENCE: Couldn't you just use one second timer, or even one variable,

  • and then just in the display [INAUDIBLE]

  • SPEAKER 1: You could.

  • Yeah, in this case, you could.

  • Again, Lua's display, it's a little bit funky when you--

  • you have to do g sub, and some weird string stuff,

  • but yes, you could do that.

  • AUDIENCE: Couldn't you just do modulo 1, and then take that value as a string?

  • Is that the equivalent of truncating?

  • SPEAKER 1: Modulo 1 would still give you the floating point value

  • because there's only one number type.

  • So if we modulo 1.00157 by 1, we'd get 0.00157.

  • AUDIENCE: Oh.

  • OK.

  • If you subtracted from that value, from I don't know.

  • So it's a value minus the value of modulo 1, I guess.

  • SPEAKER 1: So yeah.

  • It was proposed that we've used modulo, and we could.

  • In short, we could, but what if we're not just

  • printing a value to the screen.

  • What if we have 10 different things, like 10 different creatures that

  • are all doing different things over time,

  • and we don't necessarily want to have to keep a timer for each,

  • and every one of those things.

  • In a simple example like this, yeah, there's probably a--

  • on purpose, it's also a little bit convoluted just

  • to illustrate the problem.

  • But yes, there are shortcuts for this, but the fundamental problem

  • is how can we get rid of having five different timers for something?

  • And by the way, I'll go to the next slide.

  • Timer0, the simple way.

  • Timer1, the ugly way.

  • Timer2 is the clean way that I found using this ecosystem.

  • There's a wonderful library, and you could implement this yourself.

  • The fundamental idea is have a global timer

  • object that then manages all of these different things going

  • on using the power of what I alluded to earlier, anonymous functions,

  • and I'll show you how that works.

  • So in main.lua of timer2, we have a set of intervals.

  • We have a set of counters.

  • And then what we're doing here is we're just saying for i gets 1 to 5,

  • we're calling a function call timer.every.

  • So if you're familiar with JavaScript programming,

  • there's a set interval function which lets

  • you do something every length of time.

  • So first of all, timer is just a library.

  • We've just required it here.