WEBVTT X-TIMESTAMP-MAP=LOCAL:00:00:00.000,MPEGTS:900000 00:00:00.000 --> 00:00:01.984 [MUSIC PLAYING] 00:00:16.390 --> 00:00:17.170 SPEAKER 1: Hello. 00:00:17.170 --> 00:00:19.810 Welcome to lecture three of GD50. 00:00:19.810 --> 00:00:24.940 Today, we're going be talking about Match 3, as shown by the little cubes 00:00:24.940 --> 00:00:27.190 here on the slide. 00:00:27.190 --> 00:00:29.950 Match 3 originated a little earlier than 2001, 00:00:29.950 --> 00:00:32.860 but the first big game that came out that was 00:00:32.860 --> 00:00:39.092 a sort of genre staple of Match 3 was Bejeweled, shown here on the screen. 00:00:39.092 --> 00:00:41.050 This is a more modern incarnation of Bejeweled, 00:00:41.050 --> 00:00:43.090 but it came out originally in 2001. 00:00:43.090 --> 00:00:46.130 It was actually a web browser game, and the formula is very simple. 00:00:46.130 --> 00:00:52.630 The premise is you have a grid of different colored or shaped items, 00:00:52.630 --> 00:00:54.820 usually pretty small, like eight by eight, or so. 00:00:54.820 --> 00:00:57.850 And your goal is just to simply, like the name says, match three or more 00:00:57.850 --> 00:00:59.470 of them in a row. 00:00:59.470 --> 00:01:02.140 If you do, you get a certain number of points. 00:01:02.140 --> 00:01:05.080 Matching usually more than three gives you more points, or a bonus. 00:01:05.080 --> 00:01:10.150 And whenever you match three, the blocks will disappear from the grid, 00:01:10.150 --> 00:01:12.280 and they'll be replaced by more blocks. 00:01:12.280 --> 00:01:15.490 And the ones that you made holes for, the blocks above them 00:01:15.490 --> 00:01:17.269 will come down via gravity. 00:01:17.269 --> 00:01:19.310 This is a more modern incarnation of the formula. 00:01:19.310 --> 00:01:22.260 This is Candy Crush, which I think most people know. 00:01:22.260 --> 00:01:28.690 It was a very big hit on mobile devices, and otherwise around 2013, 2012, 00:01:28.690 --> 00:01:32.380 and that's probably the most recent big Match 3 style game that's come out, 00:01:32.380 --> 00:01:34.360 but there are a lot of other takes on it-- 00:01:34.360 --> 00:01:36.910 different versions that try to add new features, and stuff. 00:01:36.910 --> 00:01:40.449 This is the game that we'll be putting together today, and I'll show you how, 00:01:40.449 --> 00:01:42.490 and we'll be covering a few other things as well. 00:01:42.490 --> 00:01:45.070 So the topics today, we'll be covering, first 00:01:45.070 --> 00:01:49.450 of all, a fundamental concept in dynamic languages, a lot of dynamic languages, 00:01:49.450 --> 00:01:50.584 and also Lua. 00:01:50.584 --> 00:01:52.750 It's called anonymous functions, which are functions 00:01:52.750 --> 00:01:56.540 that are first class, meaning that they operate as data types, 00:01:56.540 --> 00:01:58.700 and so we can do some fancy stuff with those. 00:01:58.700 --> 00:02:00.640 Tweening, which means just taking one thing, 00:02:00.640 --> 00:02:04.870 and interpolating its value between two values from 1, 00:02:04.870 --> 00:02:08.120 to a destination value over time, which is a very important thing in games. 00:02:08.120 --> 00:02:09.740 You can do things like move objects. 00:02:09.740 --> 00:02:11.740 We can also tween their opacity. 00:02:11.740 --> 00:02:16.340 Just sort of asynchronous behavior, and asynchronous variable manipulation. 00:02:16.340 --> 00:02:17.710 Timers, very important. 00:02:17.710 --> 00:02:20.500 We can time something to happen at certain intervals, 00:02:20.500 --> 00:02:22.720 or after a certain length of time has passed 00:02:22.720 --> 00:02:27.280 to get us past the idea of storing different timed variables, 00:02:27.280 --> 00:02:30.840 or different counters, and break away, and keep timer objects that 00:02:30.840 --> 00:02:32.090 will take care of this for us. 00:02:32.090 --> 00:02:34.630 We'll see how we do that with a specific library. 00:02:34.630 --> 00:02:39.780 And then we'll get to the actual details of Match 3, and how to solve matches, 00:02:39.780 --> 00:02:41.200 and how to account for that. 00:02:41.200 --> 00:02:45.010 Fill in the grid, account for when we actually solve a match, 00:02:45.010 --> 00:02:46.960 and repopulate it once we've done so. 00:02:46.960 --> 00:02:49.240 We'll talk about how to do this procedurally. 00:02:49.240 --> 00:02:53.080 It's very simple compared to, I think, Breakout's more procedural layout 00:02:53.080 --> 00:02:58.040 system, but it's still randomisation, and we'll talk about that. 00:02:58.040 --> 00:03:00.820 And then last, if we have time, we'll talk about sprite art, 00:03:00.820 --> 00:03:04.210 and palettes, which is a big fundamental thing when you're doing 2D game 00:03:04.210 --> 00:03:09.220 development, and something that this, and Breakout's sprite sheet 00:03:09.220 --> 00:03:14.170 took advantage of was the idea of using, on purpose, a restricted set of colors, 00:03:14.170 --> 00:03:16.790 a palette for creating your 2D art, and there 00:03:16.790 --> 00:03:20.650 are a lot of really cool, and impressive things we can do with that. 00:03:20.650 --> 00:03:25.550 But first, I'd like to actually show what we'll be running today in class. 00:03:25.550 --> 00:03:28.300 So I'm going to come here into my directory. 00:03:28.300 --> 00:03:30.370 Make sure I'm in the right place, which I am. 00:03:30.370 --> 00:03:33.310 So this is part of the distribution code, which is online. 00:03:33.310 --> 00:03:36.370 So there's a Match 3 directory. 00:03:36.370 --> 00:03:39.250 And would anybody like to come demo it in class? 00:03:42.701 --> 00:03:45.691 All right, [? Tony. ?] Come on up. 00:03:45.691 --> 00:03:48.690 All right, so whenever you're ready, go ahead, and just hit Return here. 00:03:51.510 --> 00:03:52.920 [GAME MUSIC PLAYING] 00:03:52.920 --> 00:03:55.640 All right, and so this is my implementation of Match 3. 00:03:55.640 --> 00:03:58.620 It uses a different set of tiles. 00:03:58.620 --> 00:04:02.220 We have things that are moving over time. 00:04:02.220 --> 00:04:03.650 It's arrow key based. 00:04:07.280 --> 00:04:10.490 So if you press Enter on any tile, you can flip it with another tile. 00:04:10.490 --> 00:04:12.660 It doesn't have to be a match, in this case. 00:04:12.660 --> 00:04:16.065 So you can-- yeah. 00:04:16.065 --> 00:04:17.690 Yeah, you kind of got an unlucky board. 00:04:17.690 --> 00:04:19.898 There, at the very bottom, I see there's a few that-- 00:04:19.898 --> 00:04:22.610 some brown ones you can match together. 00:04:22.610 --> 00:04:26.579 So once you match them together, the tiles come down to repopulate. 00:04:26.579 --> 00:04:27.620 You get new tiles up top. 00:04:30.605 --> 00:04:32.730 And so notice, we have a timer on the left as well. 00:04:32.730 --> 00:04:33.810 It's something that's counting down. 00:04:33.810 --> 00:04:37.470 We'll see how this is actually done with the library we'll be using, as opposed 00:04:37.470 --> 00:04:41.850 to managing a counter variable keeping track of it over time. 00:04:47.540 --> 00:04:50.540 A lot of games will actually implement it so you have to-- you can only, 00:04:50.540 --> 00:04:52.623 and this will be part of the assignment, actually, 00:04:52.623 --> 00:04:56.160 where you can only move a tile if it creates a match. 00:04:56.160 --> 00:04:57.730 In this case, there's a-- 00:04:57.730 --> 00:05:00.980 and we can see the timer counting down, and then once you-- yeah, 00:05:00.980 --> 00:05:03.270 if you don't get past the goal, there's a game over. 00:05:03.270 --> 00:05:04.936 But thanks, [? Tony. ?] I appreciate it. 00:05:04.936 --> 00:05:05.940 AUDIENCE: No problem. 00:05:05.940 --> 00:05:07.773 SPEAKER 1: So that's the game in a nutshell. 00:05:07.773 --> 00:05:09.720 And another thing I want to point out to is 00:05:09.720 --> 00:05:13.200 the transition, the white transitions, and then the level text. 00:05:13.200 --> 00:05:16.020 Those are all done with timers that we'll be using, 00:05:16.020 --> 00:05:18.540 and tweens, which we'll be covering in class here 00:05:18.540 --> 00:05:20.505 as some of our early examples. 00:05:20.505 --> 00:05:23.130 But there's a lot of stuff that we haven't touched on, but also 00:05:23.130 --> 00:05:24.060 a lot that we have. 00:05:24.060 --> 00:05:29.140 It uses sprites, and a sprite sheet, and we've done that thing before. 00:05:29.140 --> 00:05:32.430 We chop up a sprite sheet, and then take out the whatever individual quads 00:05:32.430 --> 00:05:34.370 you need, and draw them to the screen. 00:05:34.370 --> 00:05:38.640 Here's what our goal is, which is we have a title screen with Match 3, 00:05:38.640 --> 00:05:40.080 start, and quit game in this case. 00:05:40.080 --> 00:05:40.954 A little bit simpler. 00:05:40.954 --> 00:05:43.780 No high scores this time just because we've already covered that, 00:05:43.780 --> 00:05:45.490 but we also have a level screen. 00:05:45.490 --> 00:05:48.150 It tells us what level we're on before we can actually play, 00:05:48.150 --> 00:05:51.030 and there will be a transition box with text in it. 00:05:51.030 --> 00:05:54.270 It'll come down, stop, and then come down again. 00:05:54.270 --> 00:05:59.280 So almost like chain behavior, which we'll see how we implement that too. 00:05:59.280 --> 00:06:03.510 And then lastly, on the bottom there is our main game screen, 00:06:03.510 --> 00:06:05.700 where we have a level, a score, and then a goal. 00:06:05.700 --> 00:06:10.090 If you get the goal amount of points before the timer runs out, 00:06:10.090 --> 00:06:12.180 then you go to level 2, and level 3, and level 4, 00:06:12.180 --> 00:06:16.477 and the score increases by a multiplied factor each time. 00:06:16.477 --> 00:06:18.810 So the first thing I'd like to start talking about today 00:06:18.810 --> 00:06:22.590 is how we actually get timer behavior using something a little bit more 00:06:22.590 --> 00:06:25.260 than just keeping track of some variable that we set to 0, 00:06:25.260 --> 00:06:28.210 and then adding dt to it every update. 00:06:28.210 --> 00:06:31.230 There's a better way to do that, but first, why don't we 00:06:31.230 --> 00:06:33.510 go ahead, and look at timer0. 00:06:33.510 --> 00:06:37.330 And so what I'm going to do is go into the timer0 directory. 00:06:37.330 --> 00:06:40.380 I'm going run it, and we can see here, in the middle of the screen, 00:06:40.380 --> 00:06:41.550 just a very simple-- 00:06:41.550 --> 00:06:44.460 just a label that just says timer, and then x seconds, 00:06:44.460 --> 00:06:49.290 where obviously the x is incrementing over time every second. 00:06:49.290 --> 00:06:53.250 So a crude way, what would be an easy way to implement this? 00:06:56.208 --> 00:07:00.152 AUDIENCE: Do you like [INAUDIBLE] random in Flappy Bird [INAUDIBLE] randomizer 00:07:00.152 --> 00:07:04.096 [INAUDIBLE] if you just keep track of your delta prime 00:07:04.096 --> 00:07:11.491 and add it to some variable outside [INAUDIBLE] you do something else? 00:07:11.491 --> 00:07:16.025 You could even just display the variable [INAUDIBLE].. 00:07:16.025 --> 00:07:16.650 SPEAKER 1: Yes. 00:07:16.650 --> 00:07:23.700 So the response was keep some variable that you modify with dt in update, 00:07:23.700 --> 00:07:25.110 or display the variable. 00:07:25.110 --> 00:07:26.370 Yeah, that's definitely a way. 00:07:26.370 --> 00:07:26.820 Did you have-- 00:07:26.820 --> 00:07:29.444 AUDIENCE: Yeah, I was just going to say, keep a float variable, 00:07:29.444 --> 00:07:32.146 and constantly add a dt to it, and display it, 00:07:32.146 --> 00:07:33.880 but display the truncated version. 00:07:33.880 --> 00:07:34.740 SPEAKER 1: Yeah. 00:07:34.740 --> 00:07:39.730 So keep a float variable, but just truncate the delta time off of it. 00:07:39.730 --> 00:07:41.260 You could do that, definitely. 00:07:41.260 --> 00:07:44.240 We'll take a look here as to actually how I did do it. 00:07:44.240 --> 00:07:45.720 It's very similar to that. 00:07:45.720 --> 00:07:47.220 This is the wrong directory, though. 00:07:47.220 --> 00:07:50.679 So it's in timer0 in main. 00:07:50.679 --> 00:07:51.720 So we do have a variable. 00:07:51.720 --> 00:07:57.360 So the current second here, which we are going to keep track of 0, 1, 2. 00:07:57.360 --> 00:08:02.112 Lua doesn't really have the notion of truncate a float 00:08:02.112 --> 00:08:04.320 because when you take a number that's floating point, 00:08:04.320 --> 00:08:06.411 and you make it into a string, you actually 00:08:06.411 --> 00:08:08.160 have to do string substitution on it where 00:08:08.160 --> 00:08:14.104 you use a function called g sub to take off the last part manually 00:08:14.104 --> 00:08:16.770 because it doesn't really differentiate between ints and floats. 00:08:16.770 --> 00:08:19.790 It just has a number data type. 00:08:19.790 --> 00:08:22.080 But we can do this by just keeping track of 00:08:22.080 --> 00:08:24.690 whether or not we've passed a certain length of time 00:08:24.690 --> 00:08:27.840 because we know dt is given to us in seconds. 00:08:27.840 --> 00:08:32.010 We can just add to our variable, and then every time we've 00:08:32.010 --> 00:08:34.470 gone over 1, because it gives us-- 00:08:34.470 --> 00:08:39.330 it gives you usually like .013, whatever 1/60 or approximately 1/60 of a second 00:08:39.330 --> 00:08:40.169 is. 00:08:40.169 --> 00:08:44.560 Once our timer-- we're going to keep a timer variable-- equals 1, 00:08:44.560 --> 00:08:47.400 we'll just increment current second by 1, 00:08:47.400 --> 00:08:49.586 and then we'll set that timer back to 0, and then 00:08:49.586 --> 00:08:51.210 we'll just repeat over, and over again. 00:08:51.210 --> 00:08:54.960 We'll actually use modulus so in case we go slightly over 1 second, 00:08:54.960 --> 00:08:56.400 we can account for that. 00:08:56.400 --> 00:08:57.090 We do that here. 00:08:57.090 --> 00:08:59.630 So second timer gets second timer plus delta time, 00:08:59.630 --> 00:09:01.000 and then if it's greater than 1. 00:09:01.000 --> 00:09:04.920 So if a full second has elapsed, just increment current second, 00:09:04.920 --> 00:09:07.480 and then modulo a second timer by 1. 00:09:07.480 --> 00:09:10.240 And Lua is a little different in that most languages only let 00:09:10.240 --> 00:09:13.771 you modulo something if it's an integer, but since there is no differentiation, 00:09:13.771 --> 00:09:15.520 you can actually modulo floats, and you'll 00:09:15.520 --> 00:09:17.350 get the floating point value leftover. 00:09:17.350 --> 00:09:21.035 And so that's the basic way of actually doing that, 00:09:21.035 --> 00:09:22.910 but there's a couple of things wrong with it. 00:09:22.910 --> 00:09:26.714 So does anybody want to suggest what is potentially bad or unscalable 00:09:26.714 --> 00:09:27.880 about this kind of approach? 00:09:32.921 --> 00:09:34.670 Well, I'll show you timer1 so we can maybe 00:09:34.670 --> 00:09:40.080 get a sense of how this could kind of get out of hand pretty quickly. 00:09:40.080 --> 00:09:42.890 So let's say-- first, I'll run timer1. 00:09:42.890 --> 00:09:49.820 So let's go into timer1 here, and notice now we have five labels. 00:09:49.820 --> 00:09:51.800 They're running at different intervals. 00:09:51.800 --> 00:09:55.130 The first timer, it's incrementing every 1 second, 00:09:55.130 --> 00:09:58.010 the second timer is incrementing every 4 seconds, 00:09:58.010 --> 00:10:03.800 the third one is incrementing every 4 seconds, and then so on, 3, and then 2. 00:10:03.800 --> 00:10:08.009 So if we wanted to do the same approach that we just did, 00:10:08.009 --> 00:10:09.050 this is what we would do. 00:10:09.050 --> 00:10:12.802 We have five variables, five timers. 00:10:12.802 --> 00:10:14.760 Because we want to keep track of whether or not 00:10:14.760 --> 00:10:17.504 something's gone over more than just one second, 00:10:17.504 --> 00:10:19.670 it's not super easy to just put this all in a table, 00:10:19.670 --> 00:10:22.790 and iterate over it, and use your iteration logic to do that. 00:10:22.790 --> 00:10:27.470 We actually, because they're in some sort of random, who knows what order, 00:10:27.470 --> 00:10:33.830 timer2 takes 2 seconds, OK, timer3 takes 4 seconds, timer4 takes 3, and then 2, 00:10:33.830 --> 00:10:37.209 you have to unmanageably keep all of this in separate variables. 00:10:37.209 --> 00:10:37.834 [? Yes, Tony ?] 00:10:37.834 --> 00:10:41.706 AUDIENCE: Couldn't you just use one second timer, or even one variable, 00:10:41.706 --> 00:10:46.605 and then just in the display [INAUDIBLE] 00:10:46.605 --> 00:10:47.480 SPEAKER 1: You could. 00:10:47.480 --> 00:10:49.010 Yeah, in this case, you could. 00:10:49.010 --> 00:10:54.170 Again, Lua's display, it's a little bit funky when you-- 00:10:54.170 --> 00:10:56.645 you have to do g sub, and some weird string stuff, 00:10:56.645 --> 00:10:57.770 but yes, you could do that. 00:10:57.770 --> 00:11:01.682 AUDIENCE: Couldn't you just do modulo 1, and then take that value as a string? 00:11:01.682 --> 00:11:04.130 Is that the equivalent of truncating? 00:11:04.130 --> 00:11:07.970 SPEAKER 1: Modulo 1 would still give you the floating point value 00:11:07.970 --> 00:11:09.750 because there's only one number type. 00:11:09.750 --> 00:11:14.950 So if we modulo 1.00157 by 1, we'd get 0.00157. 00:11:14.950 --> 00:11:16.450 AUDIENCE: Oh. 00:11:16.450 --> 00:11:17.450 OK. 00:11:17.450 --> 00:11:21.080 If you subtracted from that value, from I don't know. 00:11:21.080 --> 00:11:24.860 So it's a value minus the value of modulo 1, I guess. 00:11:24.860 --> 00:11:25.910 SPEAKER 1: So yeah. 00:11:25.910 --> 00:11:29.030 It was proposed that we've used modulo, and we could. 00:11:29.030 --> 00:11:33.589 In short, we could, but what if we're not just 00:11:33.589 --> 00:11:34.880 printing a value to the screen. 00:11:34.880 --> 00:11:38.840 What if we have 10 different things, like 10 different creatures that 00:11:38.840 --> 00:11:40.926 are all doing different things over time, 00:11:40.926 --> 00:11:43.550 and we don't necessarily want to have to keep a timer for each, 00:11:43.550 --> 00:11:44.799 and every one of those things. 00:11:44.799 --> 00:11:47.510 In a simple example like this, yeah, there's probably a-- 00:11:47.510 --> 00:11:49.820 on purpose, it's also a little bit convoluted just 00:11:49.820 --> 00:11:50.960 to illustrate the problem. 00:11:50.960 --> 00:11:54.770 But yes, there are shortcuts for this, but the fundamental problem 00:11:54.770 --> 00:12:00.840 is how can we get rid of having five different timers for something? 00:12:00.840 --> 00:12:02.590 And by the way, I'll go to the next slide. 00:12:02.590 --> 00:12:03.548 Timer0, the simple way. 00:12:03.548 --> 00:12:05.080 Timer1, the ugly way. 00:12:05.080 --> 00:12:10.860 Timer2 is the clean way that I found using this ecosystem. 00:12:10.860 --> 00:12:14.090 There's a wonderful library, and you could implement this yourself. 00:12:14.090 --> 00:12:17.180 The fundamental idea is have a global timer 00:12:17.180 --> 00:12:20.570 object that then manages all of these different things going 00:12:20.570 --> 00:12:25.625 on using the power of what I alluded to earlier, anonymous functions, 00:12:25.625 --> 00:12:27.000 and I'll show you how that works. 00:12:27.000 --> 00:12:32.627 So in main.lua of timer2, we have a set of intervals. 00:12:32.627 --> 00:12:33.710 We have a set of counters. 00:12:36.530 --> 00:12:42.620 And then what we're doing here is we're just saying for i gets 1 to 5, 00:12:42.620 --> 00:12:45.427 we're calling a function call timer.every. 00:12:45.427 --> 00:12:47.510 So if you're familiar with JavaScript programming, 00:12:47.510 --> 00:12:49.310 there's a set interval function which lets 00:12:49.310 --> 00:12:52.582 you do something every length of time. 00:12:52.582 --> 00:12:54.290 So first of all, timer is just a library. 00:12:54.290 --> 00:12:55.456 We've just required it here. 00:12:55.456 --> 00:13:01.040 It's part of the knife ecosystem, and then 00:13:01.040 --> 00:13:04.940 here, we have a couple of functions, timer.every, and timer.after 00:13:04.940 --> 00:13:06.530 that we'll use. 00:13:06.530 --> 00:13:09.440 Well, basically, what it does is you give it 00:13:09.440 --> 00:13:12.190 a length of time-- timer.every seconds. 00:13:12.190 --> 00:13:14.809 It's in seconds, and you can give it fractional seconds. 00:13:14.809 --> 00:13:17.600 You're passing in just a function here, just an anonymous function. 00:13:17.600 --> 00:13:21.410 It doesn't have a name, but because Lua, and a lot of dynamic languages 00:13:21.410 --> 00:13:24.750 treat functions as first class citizens, as it 00:13:24.750 --> 00:13:30.770 called, because they are data types, you can just pass them into functions. 00:13:30.770 --> 00:13:33.320 This allows us to do behavior like this that would otherwise 00:13:33.320 --> 00:13:34.700 be a little bit tricky to do. 00:13:34.700 --> 00:13:37.640 We can just say after this block of time, 00:13:37.640 --> 00:13:41.180 assuming we've built some structure that is probably just storing 00:13:41.180 --> 00:13:45.650 a table with a bunch of things that have a length of time in them, 00:13:45.650 --> 00:13:47.840 just call this block of code later. 00:13:47.840 --> 00:13:49.730 It's called a callback function. 00:13:49.730 --> 00:13:52.688 We're just going to call it back, and then we're just going to do this. 00:13:52.688 --> 00:13:55.460 We're going to say counters i gets counters i plus 1. 00:13:55.460 --> 00:13:59.322 So we have all of these intervals, and all of these counters. 00:13:59.322 --> 00:14:01.280 So it will just, basically, manage that for us, 00:14:01.280 --> 00:14:04.430 and now we don't have five variables. 00:14:04.430 --> 00:14:07.630 You do have to set whatever you want those to be. 00:14:07.630 --> 00:14:09.630 That's your that's your primitive at this point. 00:14:09.630 --> 00:14:14.245 You just need the lengths of time, depending on your problem. 00:14:14.245 --> 00:14:15.620 In this case, that's all we need. 00:14:15.620 --> 00:14:18.160 You might need more than that, depending on what you want to do with timer. 00:14:18.160 --> 00:14:20.785 But in this case, we just want to increment in value over time. 00:14:20.785 --> 00:14:23.734 So keep counters, and then just keep track of the intervals, 00:14:23.734 --> 00:14:26.150 and then our code's gone from I don't know how many lines. 00:14:26.150 --> 00:14:27.860 It was a lot larger-- 00:14:27.860 --> 00:14:35.860 I think was 96 lines, down to 98 lines, down to 70, 00:14:35.860 --> 00:14:37.800 and this is incredibly scalable. 00:14:37.800 --> 00:14:41.550 If we wanted to add another one that's 8 for example, 00:14:41.550 --> 00:14:44.370 we just need to add 8, and then that. 00:14:44.370 --> 00:14:48.830 I guess I'd have to do this as well. 00:14:48.830 --> 00:14:54.750 I gets 5 to 6, and then you want to-- the computer that requires me 00:14:54.750 --> 00:14:58.260 to off my user so I can make changes. 00:14:58.260 --> 00:15:01.090 We'll just real quickly see if I didn't mess up. 00:15:05.180 --> 00:15:08.190 And so yeah, basically we're deferring everything to timer now. 00:15:08.190 --> 00:15:12.327 We get the exact same behavior, but a much smaller length of code. 00:15:12.327 --> 00:15:14.160 And the nice thing is it's very declarative. 00:15:14.160 --> 00:15:18.330 We can just say OK, every something seconds, I want this chunk of behavior 00:15:18.330 --> 00:15:18.840 to happen. 00:15:18.840 --> 00:15:22.440 I don't have to see OK, I've got timers up here, I've got counters here. 00:15:22.440 --> 00:15:25.810 OK, down in my draw function, OK, I've got to draw all these. 00:15:25.810 --> 00:15:30.045 It's iterative, and it's declarative, and that's the ultimate goal. 00:15:30.045 --> 00:15:32.170 And here, at the very bottom, it did actually work. 00:15:32.170 --> 00:15:34.630 Now it's working every eight seconds, which is nice. 00:15:34.630 --> 00:15:37.200 One, two, there we go. 00:15:37.200 --> 00:15:39.570 Super easy to extend. 00:15:39.570 --> 00:15:42.510 We're going to be using it a lot in this problem set, 00:15:42.510 --> 00:15:46.350 and also in future lectures just because it's a lot easier than keeping 00:15:46.350 --> 00:15:50.550 track of a bunch of counter variables. 00:15:50.550 --> 00:15:52.780 And there's another function that we're seeing here-- 00:15:52.780 --> 00:15:56.670 timer.after because sometimes you just want to wait a certain length of time. 00:15:56.670 --> 00:16:00.720 Maybe you have every 1 second for 5 seconds you want a bomb to tick, 00:16:00.720 --> 00:16:03.521 and then after 5 seconds, you want it to blow up, 00:16:03.521 --> 00:16:06.520 and you could also model that with another function that we'll see soon, 00:16:06.520 --> 00:16:12.477 but these are probably the two core time-based functions. 00:16:12.477 --> 00:16:15.060 And you can go here to this URL able to see the knife library. 00:16:15.060 --> 00:16:17.950 There's a bunch of modules that are really nice. 00:16:17.950 --> 00:16:20.280 We just happen to be using timer, and it's 00:16:20.280 --> 00:16:24.240 tween, and every after functions primarily in this problem. 00:16:24.240 --> 00:16:28.020 But we'll use another one called the event in the Zelda [? p-set, ?] where 00:16:28.020 --> 00:16:31.250 we actually look at how to dispatch events, and triggers, and stuff, 00:16:31.250 --> 00:16:33.120 and to prevent us from checking every frame. 00:16:33.120 --> 00:16:34.770 Oh, what do we have to do? 00:16:34.770 --> 00:16:38.350 If some wall is broken, this frame, then do this. 00:16:38.350 --> 00:16:40.770 We can just dispatch an event that we blow up a wall. 00:16:40.770 --> 00:16:42.900 We'll get to that. 00:16:42.900 --> 00:16:46.970 Any questions at all about how those two models differ, and how they work? 00:16:49.490 --> 00:16:51.480 OK, cool. 00:16:51.480 --> 00:16:56.070 So the next thing I want to look at-- so we can tie that back into also, 00:16:56.070 --> 00:16:58.550 real quick, if we want. 00:16:58.550 --> 00:17:02.940 Match 3-- whoops, and then there's a timer that's 00:17:02.940 --> 00:17:06.859 manipulating the text on the screen. 00:17:06.859 --> 00:17:12.318 All of those letters in the-- this is in the start state of the game code. 00:17:12.318 --> 00:17:14.609 All of those letters have a color associated with them, 00:17:14.609 --> 00:17:17.940 but they're on a timer so that after every 0.075 seconds, 00:17:17.940 --> 00:17:20.183 they'll go to another color, and to another color. 00:17:20.183 --> 00:17:22.349 And so we don't have to keep track of every letter's 00:17:22.349 --> 00:17:24.359 individual color, and a timer for it. 00:17:24.359 --> 00:17:27.630 We can just change them all. 00:17:27.630 --> 00:17:33.030 If we start the game, something else that's on a timer, the timer, 00:17:33.030 --> 00:17:36.060 actually, there, which is just decrementing some value. 00:17:36.060 --> 00:17:39.434 Every one second, decrement timer by 1, and that's it, 00:17:39.434 --> 00:17:42.600 and we don't have to keep any-- we don't have to say anything more than just 00:17:42.600 --> 00:17:47.350 that, and that's what's really nice about using that kind of model. 00:17:47.350 --> 00:17:51.420 So another thing that you probably also noticed, and I'll run it again, 00:17:51.420 --> 00:17:55.800 is this fade out, and fade in, and also that animation. 00:17:55.800 --> 00:17:57.900 Those are things that are happening over time. 00:17:57.900 --> 00:18:01.290 We don't actually-- we can just sort of manipulate them. 00:18:01.290 --> 00:18:03.660 We can keep track of some sort of counter for it. 00:18:03.660 --> 00:18:07.890 We can also just say over this length of time, change this value to this value, 00:18:07.890 --> 00:18:11.050 and that's a much easier way to model the problem mentally. 00:18:11.050 --> 00:18:14.220 And so we'll illustrate that. 00:18:14.220 --> 00:18:16.200 First, I'm going to go to tween0. 00:18:16.200 --> 00:18:19.300 So tween0 is the simple way to do something. 00:18:19.300 --> 00:18:23.760 So I'm going to illustrate tweening here with Flappy Bird just going 00:18:23.760 --> 00:18:24.830 left to right. 00:18:24.830 --> 00:18:29.070 So up there, that's what happens when you print out the number, by the way, 00:18:29.070 --> 00:18:29.850 just by default. 00:18:29.850 --> 00:18:32.910 And so if you wanted to truncate it, yeah, you 00:18:32.910 --> 00:18:37.829 could just g sub the first two, I guess, depending on how large it is, 00:18:37.829 --> 00:18:40.620 and that would have the effect of displaying it as just an integer. 00:18:40.620 --> 00:18:44.220 But we can see that over two seconds, we've had-- 00:18:44.220 --> 00:18:47.400 and there's a little bit of overlap just because delta time can 00:18:47.400 --> 00:18:51.420 go a little bit over two seconds when you're adding to it because it just 00:18:51.420 --> 00:18:55.439 adds whatever length of time as a float has elapsed since the last frame. 00:18:55.439 --> 00:18:58.480 In this case, we're just adding it until it's greater than or equal to 2. 00:18:58.480 --> 00:19:02.335 So in this case, we went 0.01 over 2 by the time that actually ended. 00:19:02.335 --> 00:19:03.960 Some iterations will be less than that. 00:19:03.960 --> 00:19:06.180 So this one will be-- 00:19:06.180 --> 00:19:08.370 yeah, see, that one was less than 2.01. 00:19:08.370 --> 00:19:11.010 It just depends on your computer, and your specs. 00:19:11.010 --> 00:19:13.320 But Flappy Bird starts on the very left. 00:19:13.320 --> 00:19:16.110 So he's got an x-coordinate, and then at the very end, 00:19:16.110 --> 00:19:17.590 he's got another x-coordinate. 00:19:17.590 --> 00:19:21.735 So the simple solution is what? 00:19:28.750 --> 00:19:32.060 We know that we want this to elapse over two seconds. 00:19:32.060 --> 00:19:35.550 So what we can do is I'm going to pull up tween0. 00:19:42.536 --> 00:19:44.910 And MOVE_DURATION here, it's a constant of 2 seconds just 00:19:44.910 --> 00:19:46.865 for the sake of this example. 00:19:46.865 --> 00:19:48.930 A sprite here, just a simple image. 00:19:48.930 --> 00:19:53.610 I'm putting everything in one code file this time, as opposed to breaking it 00:19:53.610 --> 00:19:57.849 out into subclasses just for simplicity because these are such small examples. 00:19:57.849 --> 00:19:59.140 But we're setting it's x and y. 00:19:59.140 --> 00:20:01.056 Oh, and this is another Lua trick, by the way. 00:20:01.056 --> 00:20:05.490 You can assign two variables to two values using a comma here. 00:20:05.490 --> 00:20:08.720 So flappyX comma flappyY gets 0, and then VIRTUAL_HEIGHT 00:20:08.720 --> 00:20:10.440 divided by 2 minus 8. 00:20:10.440 --> 00:20:15.484 Setting this x to 0, we have a timer here, and then it's end x. 00:20:15.484 --> 00:20:17.400 So we want it to end at the end of the screen. 00:20:17.400 --> 00:20:20.880 So we're going to say virtual width minus his width, an then 00:20:20.880 --> 00:20:24.150 the usual boilerplate for getting a project set to go. 00:20:24.150 --> 00:20:30.390 If it's less than the move duration-- so if timer is 0 going up to 2, 00:20:30.390 --> 00:20:37.950 but it's not quite 2 yet, we're going to add dt to it, and then we're going to, 00:20:37.950 --> 00:20:43.230 basically, assign it to either the lowest of end x, 00:20:43.230 --> 00:20:49.500 so it will never go higher than end x, or end x times the ratio of timer 00:20:49.500 --> 00:20:51.030 over move duration. 00:20:51.030 --> 00:20:54.480 So timer over move duration, if it's less than 2, 00:20:54.480 --> 00:20:59.070 that's going to be some value, some fractional amount less than 1. 00:20:59.070 --> 00:21:01.620 So it's going to basically just scale it, 00:21:01.620 --> 00:21:07.780 depending on how far we've moved the timer between 0 and 2. 00:21:07.780 --> 00:21:09.710 So just a scaling operation. 00:21:09.710 --> 00:21:13.680 This happens to only work in the context of moving something from left to right, 00:21:13.680 --> 00:21:18.480 or from 0 to something else, but it's a crude, basic way of illustrating 00:21:18.480 --> 00:21:20.261 a very basic tween operation. 00:21:20.261 --> 00:21:21.510 That's what it essentially is. 00:21:21.510 --> 00:21:25.440 It's a multiplier of some ratio of how much time has passed, 00:21:25.440 --> 00:21:28.770 versus how much time we're actually looking to elapse. 00:21:28.770 --> 00:21:31.830 And that has the effect, once again, of just-- 00:21:31.830 --> 00:21:35.750 it's scaling the ratio because it's timer over moved duration. 00:21:35.750 --> 00:21:38.130 It's something over 2, but it's not quite 2 over 2. 00:21:38.130 --> 00:21:42.690 Until it gets 2 over 2, and it's 1, then end x times 1 is going to be end x. 00:21:42.690 --> 00:21:47.670 But before that, it's going to be some fraction of end x between 0 and end x. 00:21:47.670 --> 00:21:51.960 So it has the effect of giving us a very basic tween, but it's a little bit-- 00:21:51.960 --> 00:21:54.810 we have a little bit to manage here. 00:21:54.810 --> 00:21:56.670 It doesn't really feel super clean. 00:21:56.670 --> 00:22:00.360 Do you guys have any questions about how this works? 00:22:00.360 --> 00:22:01.800 OK. 00:22:01.800 --> 00:22:04.450 So we're going to go here. 00:22:04.450 --> 00:22:11.460 So first of all, any thoughts about how that might not be super scalable, 00:22:11.460 --> 00:22:13.080 looking back at the last example? 00:22:17.850 --> 00:22:20.699 AUDIENCE: I guess like the situation before, 00:22:20.699 --> 00:22:22.615 getting a lot of objects moving on the screen. 00:22:22.615 --> 00:22:25.698 SPEAKER 1: Yeah, and what if your index is different for every single one? 00:22:25.698 --> 00:22:28.470 Then you have kind of a mess. 00:22:28.470 --> 00:22:31.020 What if we had something like this? 00:22:34.110 --> 00:22:39.090 You don't want to keep track of an end to x. 00:22:39.090 --> 00:22:41.596 They all happened to have the same end x, 00:22:41.596 --> 00:22:43.470 but notice they're moving at different rates. 00:22:43.470 --> 00:22:47.025 They're all moving at some sort of random amount. 00:22:47.025 --> 00:22:48.240 AUDIENCE: How many are there? 00:22:48.240 --> 00:22:52.680 SPEAKER 1: There's 1,000. 00:22:52.680 --> 00:22:54.840 We could go crazier if we wanted. 00:22:54.840 --> 00:22:58.540 This is a fun thing to do, is stress testing. 00:22:58.540 --> 00:23:04.000 So if I go to tween1, and I go to main, timer max is 10. 00:23:04.000 --> 00:23:06.540 So we're saying that the longest possible time 00:23:06.540 --> 00:23:11.130 any bird can take to get from left to right is going be 10 seconds. 00:23:11.130 --> 00:23:14.310 So right here, we're using a table based approach here. 00:23:14.310 --> 00:23:17.830 We're actually keeping track of 1,000 birds, and we're saying, 00:23:17.830 --> 00:23:20.220 OK, here's an empty table from 1 to 1,000. 00:23:20.220 --> 00:23:23.480 Add a new bird, and in this case, we're not adding a bird object, or anything. 00:23:23.480 --> 00:23:24.750 It's just a table. 00:23:24.750 --> 00:23:26.970 They all start x equals 0, left side. 00:23:26.970 --> 00:23:28.485 Their y is random. 00:23:28.485 --> 00:23:31.360 So they can be anywhere between the top and the bottom of the screen. 00:23:31.360 --> 00:23:32.484 So VIRTUAL_HEIGHT minus 24. 00:23:32.484 --> 00:23:34.710 24 happens to be the height of the sprite, 00:23:34.710 --> 00:23:37.677 and I should have probably put Flappy sprite get height right there. 00:23:37.677 --> 00:23:40.010 And then rate, they're all going to have their own rate. 00:23:40.010 --> 00:23:41.370 So rate gets math.random. 00:23:41.370 --> 00:23:44.790 Math.random without a value passed into it 00:23:44.790 --> 00:23:52.030 gives you a fractional value between 0 and 0.999999. 00:23:52.030 --> 00:23:55.500 What this has the effect of doing is math.random with just two values, 00:23:55.500 --> 00:23:58.500 if you pass in 10 and 50, it's going to give you 10 to 50, 00:23:58.500 --> 00:24:00.880 but they're always going to be integer values. 00:24:00.880 --> 00:24:03.830 You can't say like 10.0 and 50.0, and assume that it 00:24:03.830 --> 00:24:05.330 will know what you're talking about. 00:24:05.330 --> 00:24:06.790 It's just going to be integers. 00:24:06.790 --> 00:24:08.880 So if you give it one value, it will know. 00:24:08.880 --> 00:24:13.050 OK, you're asking me for a float between 0 and 0.999999. 00:24:13.050 --> 00:24:16.230 That's going to act as the fractional part of whatever value we 00:24:16.230 --> 00:24:20.500 might want to generate using a math.random with a value passed in. 00:24:20.500 --> 00:24:24.870 So here, we're saying OK, math.random, TIMER_MAX minus 1. 00:24:24.870 --> 00:24:26.580 So TIMER_MAX is 9. 00:24:26.580 --> 00:24:29.460 So our TIMER_MAX is 10, sorry. 00:24:29.460 --> 00:24:33.630 So if we subtract 1 from that, this is math.random 10. 00:24:33.630 --> 00:24:37.170 So we're going to get a value between 1 and 9. 00:24:37.170 --> 00:24:38.880 So TIMER_MAX minus 1 is 9. 00:24:38.880 --> 00:24:39.990 Sorry if I said 10. 00:24:39.990 --> 00:24:43.260 TIMER_MAX is 10, TIMER_MAX minus 1 is 9, but we're 00:24:43.260 --> 00:24:46.500 adding math.random, some fractional amount. 00:24:46.500 --> 00:24:49.110 So whatever value we choose between 1-- 00:24:49.110 --> 00:24:53.460 actually between-- yeah, between 1 and 9, 00:24:53.460 --> 00:24:58.170 it's going to be that value, point something. 00:24:58.170 --> 00:24:59.670 So this is how you get random-- 00:24:59.670 --> 00:25:01.378 basically, at the end of the day, this is 00:25:01.378 --> 00:25:07.265 how you get random floating point numbers in Lua and Love2D. 00:25:07.265 --> 00:25:08.140 Does that make sense? 00:25:08.140 --> 00:25:09.723 Do you guys have questions about that? 00:25:09.723 --> 00:25:12.420 AUDIENCE: So your final rate could be anywhere from 0 to-- 00:25:12.420 --> 00:25:15.461 SPEAKER 1: Final rate is going to be anywhere from 1-- in this case from, 00:25:15.461 --> 00:25:19.620 1 to 9.999999. 00:25:19.620 --> 00:25:22.450 If we wanted it to be 0, we could do that, 00:25:22.450 --> 00:25:26.520 and that'll give us the effect of taking whatever we get, and subtracting 1. 00:25:26.520 --> 00:25:32.070 So now it will be between 0 and 8.999999. 00:25:32.070 --> 00:25:32.970 And if we did this-- 00:25:35.650 --> 00:25:39.890 AUDIENCE: Couldn't each math.random end up at 0? 00:25:39.890 --> 00:25:43.110 SPEAKER 1: No, because math.random, if you pass in a value, 00:25:43.110 --> 00:25:46.036 it'll always do from 1 to some value. 00:25:46.036 --> 00:25:50.310 The question was will math.random give you 0 if you pass in a value? 00:25:50.310 --> 00:25:54.560 And math.random, by default, gives you between 1 and something else. 00:25:54.560 --> 00:25:57.027 And so that's why we do this. 00:25:57.027 --> 00:25:58.110 That's not why we do this. 00:25:58.110 --> 00:26:00.990 We do this to add the fractional part so that we can get 00:26:00.990 --> 00:26:04.110 fractional floats between some value. 00:26:04.110 --> 00:26:07.132 But yeah, if you wanted it to be between 0, and something else, 00:26:07.132 --> 00:26:09.090 you would just subtract 1 from the final result 00:26:09.090 --> 00:26:12.900 because we know that we're always going to get from 1 to some value. 00:26:12.900 --> 00:26:15.120 If you minus from 1, you will never go below 0. 00:26:15.120 --> 00:26:17.799 It will always be 0 to something else, and then in that case, 00:26:17.799 --> 00:26:19.590 we probably would just take off the minus 1 00:26:19.590 --> 00:26:26.640 from TIMER_MAX so that it will be between 0 and 9.999999 in this case. 00:26:26.640 --> 00:26:29.732 But as a design decision, I made it so that we would always 00:26:29.732 --> 00:26:31.440 have at least a rate of 1 because then it 00:26:31.440 --> 00:26:36.820 could get really, really slow if it's like 0.005 or something like that. 00:26:36.820 --> 00:26:37.860 You wouldn't want that. 00:26:37.860 --> 00:26:38.651 It would take ages. 00:26:42.510 --> 00:26:45.680 OK, so we have a timer. 00:26:45.680 --> 00:26:50.095 We're not using knife.timer in this case yet. 00:26:50.095 --> 00:26:53.220 Basically, in update, we're just saying as long as timer max is less than-- 00:26:53.220 --> 00:26:55.980 timer is less than timer max. 00:26:55.980 --> 00:27:00.560 This update logic will only run as long as we haven't gone over timer max. 00:27:00.560 --> 00:27:04.532 Increment it, and then for each bird, basically, we're 00:27:04.532 --> 00:27:06.990 doing the same thing that we did before, except we're using 00:27:06.990 --> 00:27:09.770 that bird's rate as the scale factor. 00:27:09.770 --> 00:27:13.560 Yeah, the denominator of that ratio, and that 00:27:13.560 --> 00:27:16.710 will have the effect of multiplying all of those birds 00:27:16.710 --> 00:27:19.230 individually, based on their own rate, rather than 00:27:19.230 --> 00:27:23.190 some global rate, if that makes sense. 00:27:23.190 --> 00:27:27.260 And then here, we're just drawing all of them at their own x and y. 00:27:27.260 --> 00:27:30.630 And just again, we're just storing birds are just 00:27:30.630 --> 00:27:32.670 a table with a few variables in this case. 00:27:32.670 --> 00:27:36.540 Just a very simple shell, and it has the effect of-- 00:27:36.540 --> 00:27:41.570 oh, and one thing I wanted to do is just add a 0 there. 00:27:41.570 --> 00:27:43.890 I've got to do this every time because I didn't 00:27:43.890 --> 00:27:45.802 set my permissions appropriately. 00:27:45.802 --> 00:27:47.510 But now there's going to be 10,000 birds. 00:27:47.510 --> 00:27:50.260 So let's see how this looks. 00:27:50.260 --> 00:27:53.820 So it looks pretty similar, actually, but it's a little more condensed now. 00:27:53.820 --> 00:27:55.950 I don't notice a frame rate drop. 00:27:55.950 --> 00:28:04.340 If we wanted, we could go down to love.draw, and love.graphics.printf 00:28:04.340 --> 00:28:15.396 at FPS, and then to string, love.timer.getFPS, and then 00:28:15.396 --> 00:28:24.330 let's set it to 4, and then VIRTUAL_HEIGHT minus 16, 00:28:24.330 --> 00:28:26.220 and then got to do this again. 00:28:26.220 --> 00:28:28.990 This should have the effect of giving us our frame rate. 00:28:28.990 --> 00:28:34.580 So we can really stress test this, and see what the-- 00:28:34.580 --> 00:28:35.080 oh. 00:28:35.080 --> 00:28:36.670 I made a mistake. 00:28:36.670 --> 00:28:37.440 OK, let's see. 00:28:37.440 --> 00:28:38.820 What did I do? 00:28:38.820 --> 00:28:40.830 Push.start. 00:28:40.830 --> 00:28:42.030 I'm guessing I missed a-- 00:28:42.030 --> 00:28:43.176 yeah, I missed a-- 00:28:46.830 --> 00:28:51.970 do that, and then that, and then save it again. 00:28:51.970 --> 00:28:52.760 Sorry about that. 00:28:52.760 --> 00:28:54.968 There won't be too many of these edits, but I figured 00:28:54.968 --> 00:28:56.670 this would be fun to illustrate. 00:28:56.670 --> 00:28:59.100 So now, this should work. 00:29:01.630 --> 00:29:05.224 Printf, so we have that. 00:29:05.224 --> 00:29:08.040 Oh, right, and then it just needs to be print. 00:29:08.040 --> 00:29:10.830 It doesn't need to be printf. 00:29:10.830 --> 00:29:12.750 Got to save again. 00:29:12.750 --> 00:29:14.450 Sorry. 00:29:14.450 --> 00:29:18.220 This will be worth it, I hope. 00:29:18.220 --> 00:29:20.326 There we go, 5160. 00:29:20.326 --> 00:29:21.700 OK, it takes a couple of seconds. 00:29:21.700 --> 00:29:24.540 It has to interpolate between the last couple of times 00:29:24.540 --> 00:29:26.964 that it's pulled for frames, and when you start up, 00:29:26.964 --> 00:29:28.380 it doesn't have the data it needs. 00:29:28.380 --> 00:29:31.620 So 10,000, easy. 00:29:31.620 --> 00:29:34.360 Let's do like a million. 00:29:34.360 --> 00:29:39.480 So we have 10,000, 100,000, a million. 00:29:39.480 --> 00:29:41.660 I really got to change those permissions. 00:29:41.660 --> 00:29:44.440 Getting good at practicing my password, though. 00:29:44.440 --> 00:29:46.868 Going to go ahead, and do that. 00:29:46.868 --> 00:29:48.150 Ooh. 00:29:48.150 --> 00:29:50.640 Oh, my laptop is suffering. 00:29:50.640 --> 00:29:53.700 Oh man, but they're all moving independent of frame rate. 00:29:53.700 --> 00:29:56.910 This is just a testament to how powerful delta time is. 00:29:56.910 --> 00:29:59.070 They all move there after 10 seconds. 00:29:59.070 --> 00:30:01.670 We've got to 10.6 seconds, which is not good. 00:30:01.670 --> 00:30:04.470 That means that's how long passed between the last frame, 00:30:04.470 --> 00:30:08.850 but my laptop clearly can not handle a million birds on the screen, 00:30:08.850 --> 00:30:12.450 but it can handle 10,000, or maybe even 100,000. 00:30:12.450 --> 00:30:13.740 And that's just a fun way-- 00:30:13.740 --> 00:30:15.420 a fun thing to do. 00:30:15.420 --> 00:30:18.210 In general, when you want to stress test your game, 00:30:18.210 --> 00:30:21.210 just put the frames per second on, and just go nuts. 00:30:21.210 --> 00:30:22.740 Just add a lot of stuff. 00:30:22.740 --> 00:30:25.710 Just see what your computer is capable of because you 00:30:25.710 --> 00:30:29.630 can find new, fun things that way, I guess, and maybe just 00:30:29.630 --> 00:30:32.840 see how good your code is too. 00:30:32.840 --> 00:30:35.100 So tween0, simple way. 00:30:35.100 --> 00:30:38.490 We have just variables, and one counter. 00:30:38.490 --> 00:30:40.110 No tables or anything. 00:30:40.110 --> 00:30:43.969 Tween2 is a good way for if we have a lot of things 00:30:43.969 --> 00:30:45.510 that we want to manipulate over time. 00:30:45.510 --> 00:30:49.650 But what if now we want some of them to change 00:30:49.650 --> 00:30:52.950 their opacity over time or something? 00:30:52.950 --> 00:30:55.241 It starts to get a little bit more complicated. 00:30:55.241 --> 00:30:57.990 And this is, by the way, I should've put this earlier in the code, 00:30:57.990 --> 00:31:03.360 but this is the knife library is responsible for timer, 00:31:03.360 --> 00:31:05.806 and a lot of other things that we'll be looking at, 00:31:05.806 --> 00:31:07.680 and it's got a bunch of modules here listed-- 00:31:07.680 --> 00:31:09.986 behavior for state machines, which is like what 00:31:09.986 --> 00:31:13.110 we've been doing for state machines, but they have their own version of it. 00:31:13.110 --> 00:31:16.290 Knife.bind, so you can pre-bind arguments to functions, 00:31:16.290 --> 00:31:18.230 and create subfunction. 00:31:18.230 --> 00:31:20.940 It's called currying, but create subfunctions of other functions 00:31:20.940 --> 00:31:23.750 that have pre-determined variables. 00:31:23.750 --> 00:31:28.740 Knife.chain, we'll see, actually, how that can be used coming up later. 00:31:28.740 --> 00:31:30.570 Convoke is for coroutines. 00:31:30.570 --> 00:31:32.520 We'll see coroutines in the context of Unity, 00:31:32.520 --> 00:31:36.570 but basically, they're functions that can pause their state for later. 00:31:36.570 --> 00:31:41.580 Knife.event we'll use in two weeks for Zelda, 00:31:41.580 --> 00:31:44.510 maybe even next week if I can fit it in for Mario. 00:31:44.510 --> 00:31:46.980 Memoize is for memoization. 00:31:46.980 --> 00:31:49.810 It's like a dynamic programming related thing. 00:31:49.810 --> 00:31:51.720 Serialized system. 00:31:51.720 --> 00:31:55.620 System is going to be useful to know about in context of Unity as well. 00:31:55.620 --> 00:31:59.230 Unity uses an entity component system for much of its structure. 00:31:59.230 --> 00:32:01.830 Knife.test, and then lastly, knife.timer, 00:32:01.830 --> 00:32:05.970 which is what we'll end up using, and this is probably my favorite library 00:32:05.970 --> 00:32:09.290 that exists in the Love2D ecosystem. 00:32:09.290 --> 00:32:12.540 And so with that said, we'll look at tween2 now. 00:32:12.540 --> 00:32:22.620 I'm going to go here into tween2, and so now we have not just movement, but also 00:32:22.620 --> 00:32:23.790 their opacity. 00:32:23.790 --> 00:32:26.460 They all start at different opacity levels, 00:32:26.460 --> 00:32:30.870 and we want to not only change their movement 00:32:30.870 --> 00:32:34.110 over time, but also the opacity. 00:32:34.110 --> 00:32:39.247 So it would get a little bit trickier if we decided 00:32:39.247 --> 00:32:40.830 to do that with our current situation. 00:32:40.830 --> 00:32:47.090 Totally doable, but how would we go about changing, just right now, 00:32:47.090 --> 00:32:50.370 their opacity just as is? 00:32:50.370 --> 00:32:54.555 How do you change a spite's opacity? 00:32:54.555 --> 00:33:01.897 AUDIENCE: The variable and the graphics [INAUDIBLE] 00:33:01.897 --> 00:33:04.438 SPEAKER 1: Is it a variable-- can you say that one more time? 00:33:04.438 --> 00:33:06.229 AUDIENCE: I forget the exact function name, 00:33:06.229 --> 00:33:13.365 but it's like love.graphics to put an image on the screen. 00:33:13.365 --> 00:33:15.390 SPEAKER 1: The love.graphics.draw. 00:33:15.390 --> 00:33:18.900 AUDIENCE: Wasn't there an argument in that? 00:33:18.900 --> 00:33:22.830 SPEAKER 1: So it's actually not an argument to that function. 00:33:22.830 --> 00:33:24.120 So I'll show you now. 00:33:24.120 --> 00:33:27.930 So in order to draw something at some different opacity, 00:33:27.930 --> 00:33:33.250 it's actually love.graphics.setcolor, and we do that here. 00:33:33.250 --> 00:33:38.760 So recall that Love2D is a state machine, and how it draws things. 00:33:38.760 --> 00:33:41.040 You can basically set a color onto anything 00:33:41.040 --> 00:33:47.520 that you draw, whether it's a font, an image, or a shape. 00:33:47.520 --> 00:33:53.432 And if you just pass in 255, 255, 255, that's white. 00:33:53.432 --> 00:33:55.140 And then if you give it an opacity, which 00:33:55.140 --> 00:33:58.590 is the fourth parameter, which is the alpha component of that, 00:33:58.590 --> 00:34:00.890 then that's how transparent it will be. 00:34:00.890 --> 00:34:03.570 And so we could have done this with other colors too. 00:34:03.570 --> 00:34:06.153 We could have done this with like if we wanted to tint it red, 00:34:06.153 --> 00:34:07.890 and also have it be sort of transparent. 00:34:07.890 --> 00:34:12.010 We could do that if we just did 25500 bird.opacity. 00:34:12.010 --> 00:34:14.580 But if you just want to manipulate opacity independent of-- 00:34:14.580 --> 00:34:18.389 or its transparency or its alpha independent of its color, 00:34:18.389 --> 00:34:21.469 and keep it the same exact color, you just do it white. 00:34:21.469 --> 00:34:25.139 If you did it black, nothing would show up. 00:34:25.139 --> 00:34:26.219 Is that true, actually? 00:34:26.219 --> 00:34:27.010 Let me verify that. 00:34:29.940 --> 00:34:34.350 Pretty sure that's right, but I could be wrong. 00:34:38.860 --> 00:34:40.340 I'm right, thankfully. 00:34:40.340 --> 00:34:41.830 OK. 00:34:41.830 --> 00:34:44.931 That or they're just black, and there's a black background too. 00:34:44.931 --> 00:34:46.389 Do you have a question, [? Tony? ?] 00:34:46.389 --> 00:34:46.930 AUDIENCE: No. 00:34:46.930 --> 00:34:49.300 SPEAKER 1: Oh, OK. 00:34:49.300 --> 00:34:58.780 So we'll take it on faith that that is correct. 00:34:58.780 --> 00:35:06.391 And back to the gist of this example. 00:35:06.391 --> 00:35:07.390 We have TIMER_MAX again. 00:35:07.390 --> 00:35:10.250 Actually, we really haven't changed much. 00:35:10.250 --> 00:35:14.560 What we have changed is we still have our birds. 00:35:14.560 --> 00:35:18.450 We need to keep track of their x, of their y, of their rate. 00:35:18.450 --> 00:35:19.887 Well, not necessarily their rate. 00:35:19.887 --> 00:35:21.970 Oh, well, their rate, yeah, because we're actually 00:35:21.970 --> 00:35:24.011 going to loop over each of these, and then create 00:35:24.011 --> 00:35:26.020 a timer between operations for them. 00:35:26.020 --> 00:35:28.160 And their opacity. 00:35:28.160 --> 00:35:29.980 Oh right, they start with an opacity of 0, 00:35:29.980 --> 00:35:35.500 and faded to 55, regardless of their-- 00:35:38.820 --> 00:35:43.150 their opacity changes at the same rate as their x does. 00:35:43.150 --> 00:35:44.950 So the farther away, the longer they take, 00:35:44.950 --> 00:35:49.510 the slower they fade to fully opaque, and we see this here. 00:35:49.510 --> 00:35:51.855 So for k bird and pairs of birds. 00:35:51.855 --> 00:35:55.490 So for every bird, we're just going to set a tween, and then this is tween. 00:35:55.490 --> 00:36:00.550 So timer.tween is I think I have a slide on it here, right? 00:36:00.550 --> 00:36:05.710 A super cool useful function, super easy to use too, it 00:36:05.710 --> 00:36:10.940 takes iteration just like timer.every, timer.after, and it takes a definition. 00:36:10.940 --> 00:36:13.450 So in this case, it doesn't take an anonymous function 00:36:13.450 --> 00:36:15.450 like the other ones did because we're not really 00:36:15.450 --> 00:36:19.354 saying I want to do some sort of undefined behavior 00:36:19.354 --> 00:36:20.770 over the course of this operation. 00:36:20.770 --> 00:36:23.140 What I want to do is just change some values. 00:36:23.140 --> 00:36:25.070 I want to interpolate them. 00:36:25.070 --> 00:36:31.090 So what we're going to do is just pass in a-- this is the syntax for it. 00:36:31.090 --> 00:36:35.820 We pass in square brackets, the actual thing that we want to change. 00:36:35.820 --> 00:36:37.900 In this case, I want to change bird. 00:36:37.900 --> 00:36:41.620 I want bird to change in some way, and then 00:36:41.620 --> 00:36:44.590 what I want it's values to change toward are these. 00:36:44.590 --> 00:36:50.230 I want it's x to change to end x, and I want it's opacity to go to 255. 00:36:50.230 --> 00:36:54.019 And I wanted to do it over bird.rate, and so this bird.rate, 00:36:54.019 --> 00:36:55.060 every bird is storing it. 00:36:55.060 --> 00:36:57.850 So for birds that got a rate of 2, then it's 00:36:57.850 --> 00:37:01.630 x is going to go to end x over the course of 2 seconds, 00:37:01.630 --> 00:37:06.730 and it's opacity is going to go to 255 over the course of 2 seconds. 00:37:06.730 --> 00:37:10.620 And you can put as many things as you want, and as many-- 00:37:10.620 --> 00:37:15.334 you can put as many variables here, and as many entities. 00:37:15.334 --> 00:37:17.500 Entities being anything that you want to change that 00:37:17.500 --> 00:37:21.220 has a field, any table-based or class-based structure. 00:37:21.220 --> 00:37:25.810 You can pass any of those in here, and just tween them all at the same-- 00:37:25.810 --> 00:37:29.830 if they all have the same rate, and then just get that operation that way. 00:37:29.830 --> 00:37:33.040 And so that has the effect here of all we need to do is just add-- 00:37:33.040 --> 00:37:36.370 it's like two lines of code, but now we've easily changed it so 00:37:36.370 --> 00:37:40.067 that we can just tween two things at once, 00:37:40.067 --> 00:37:42.400 and that's the power of timer.tween, and we'll see that. 00:37:42.400 --> 00:37:47.140 So back to, actually, Match 3, if we want to look at that again. 00:37:49.660 --> 00:37:54.190 This is a tween, that's a tween, that's a tween, and that's a tween. 00:37:54.190 --> 00:37:59.980 So the white, the foreground there, is just a rectangle 00:37:59.980 --> 00:38:01.320 that fills the whole screen. 00:38:01.320 --> 00:38:03.550 It's just a white rectangle. 00:38:03.550 --> 00:38:08.770 I have it set to timer.tween opacity from 0 to 255. 00:38:08.770 --> 00:38:13.570 Before that gets called, if we go from the start to the begin game state, 00:38:13.570 --> 00:38:18.550 and then if we go from at the beginning of the game state, 00:38:18.550 --> 00:38:23.160 before the level text comes down, it's going from 255 to 0. 00:38:23.160 --> 00:38:26.810 So it's just the reverse of that is the tween, and then all it's doing 00:38:26.810 --> 00:38:28.560 is just drawing a rectangle to the screen. 00:38:28.560 --> 00:38:32.502 But that's how you get a very simple transition. 00:38:32.502 --> 00:38:33.710 Same thing for fade to black. 00:38:33.710 --> 00:38:35.080 If you want to fade the whole screen to black, 00:38:35.080 --> 00:38:37.310 just draw a rectangle the size of the screen, 00:38:37.310 --> 00:38:42.995 and then just tween its opacity from 0 to 255, and then vise versa. 00:38:42.995 --> 00:38:44.620 That's how you get a simple transition. 00:38:44.620 --> 00:38:45.828 It can be any color you want. 00:38:45.828 --> 00:38:48.280 It can be a red transition. 00:38:48.280 --> 00:38:54.970 And then the level text, that's just a tween on the y, right? 00:38:54.970 --> 00:38:59.740 And then I just have some rectangle, love.graphics.rectangle with text, 00:38:59.740 --> 00:39:04.600 and it just says timer.tween to like VIRTUAL_HEIGHT divided by 2 minus 8, 00:39:04.600 --> 00:39:08.260 and then timer.after1. 00:39:08.260 --> 00:39:10.650 So we can actually pull this up if we want. 00:39:10.650 --> 00:39:14.070 We can see how this works. 00:39:14.070 --> 00:39:17.980 Today's going to be a little wider on the main distro code 00:39:17.980 --> 00:39:20.950 just because a lot of this is more conceptual. 00:39:20.950 --> 00:39:27.430 But in the begin game state-- well, actually, in the start state 00:39:27.430 --> 00:39:29.120 is when we go. 00:39:29.120 --> 00:39:33.130 So these colors, and letter table, and stuff that's all for the Match 3 text, 00:39:33.130 --> 00:39:34.060 if you're curious. 00:39:34.060 --> 00:39:37.960 So these are all back to what I said earlier about the beginning screen 00:39:37.960 --> 00:39:41.310 having Match 3 with the different colors going on a timer. 00:39:41.310 --> 00:39:43.250 These are just tables of colors. 00:39:43.250 --> 00:39:47.140 So notice this is RGBA, and then I'm just performing 00:39:47.140 --> 00:39:49.550 a shuffle on them every 0.075 seconds. 00:39:49.550 --> 00:39:52.630 So 2 will get 1, and vise versa. 00:39:52.630 --> 00:39:58.390 It'll all go down, and then 6 will come up here to 1 every 0.75 seconds. 00:39:58.390 --> 00:40:01.140 And then M gets mapped to this one, A gets mapped to this one, 00:40:01.140 --> 00:40:05.080 T to this one, C, H, 3, and that's it. 00:40:05.080 --> 00:40:09.190 That's done here at line 44 of the start state. 00:40:09.190 --> 00:40:16.120 But what I was going to show you was the tween for the transitions. 00:40:16.120 --> 00:40:21.730 So here in start state, in the update function says, 00:40:21.730 --> 00:40:24.690 it says if we press Enter, and our current menu item 00:40:24.690 --> 00:40:29.960 is 1, meaning that we're on start game, not quit game, timer.tween here. 00:40:29.960 --> 00:40:33.760 And notice that we have a finish function, which will show-- 00:40:33.760 --> 00:40:37.210 I'm actually going to show you in the next couple of examples, the chain 00:40:37.210 --> 00:40:38.350 examples. 00:40:38.350 --> 00:40:40.360 But finish is just a function that you can 00:40:40.360 --> 00:40:43.930 run after any timer that just says, hey, when this is finished, 00:40:43.930 --> 00:40:47.330 run this block of code, and notice that takes anonymous function here, 00:40:47.330 --> 00:40:48.070 just like that. 00:40:48.070 --> 00:40:52.337 So we can say OK, tween, over the course of one second, 00:40:52.337 --> 00:40:55.420 notice we're passing self into here because we want to manipulate ourself. 00:40:55.420 --> 00:40:57.211 We have a value that we want to manipulate. 00:40:57.211 --> 00:40:59.350 So self.transitionAlpha. 00:40:59.350 --> 00:41:02.290 So we're saying I want to take my transition alpha, 00:41:02.290 --> 00:41:07.240 and I want it to go to 255, and we set it to 0 by default. So at the very top, 00:41:07.240 --> 00:41:13.100 here, at line 60, transition alpha is just our white rectangle that 00:41:13.100 --> 00:41:14.160 fills the screen. 00:41:14.160 --> 00:41:17.256 I'm just saying set it to 0 so we don't see it at all. 00:41:17.256 --> 00:41:18.380 It's going to be invisible. 00:41:18.380 --> 00:41:20.150 It's still there no matter what. 00:41:20.150 --> 00:41:23.450 It's hidden, but after we press Enter, tween 00:41:23.450 --> 00:41:27.770 it to 255 over the course of 1 second, and then when that's finished, 00:41:27.770 --> 00:41:29.580 notice this is familiar, right? 00:41:29.580 --> 00:41:31.640 gStateMachine change begin game. 00:41:31.640 --> 00:41:33.990 We're going to go with the begin game state after that. 00:41:33.990 --> 00:41:35.150 Our passing level gets 1. 00:41:35.150 --> 00:41:38.810 We're starting the game, and then we're going to remove this color. 00:41:38.810 --> 00:41:41.780 Remove that timer from the-- this is actually 00:41:41.780 --> 00:41:47.330 unnecessary in this circumstance, but you can remove timers from timer. 00:41:47.330 --> 00:41:49.970 If you have something going constantly-- 00:41:49.970 --> 00:41:53.180 in this case, the color timer, and let's say we move from this state 00:41:53.180 --> 00:41:55.940 to the next state, the next state doesn't have all those colors, 00:41:55.940 --> 00:41:58.320 right, the Match 3 colors. 00:41:58.320 --> 00:42:01.100 So we don't need to keep-- because timer is a global object, 00:42:01.100 --> 00:42:05.120 it's going to keep updating over, and over again. 00:42:05.120 --> 00:42:07.940 We don't need certain timers to exist indefinitely. 00:42:07.940 --> 00:42:11.300 We can just remove this one because it's not relevant anymore. 00:42:11.300 --> 00:42:14.990 But this is all it takes just to give us a simple transition from one screen 00:42:14.990 --> 00:42:15.800 to another. 00:42:15.800 --> 00:42:23.320 Just give this transition alpha 255 down in the actual render function. 00:42:23.320 --> 00:42:24.510 Where is it? 00:42:24.510 --> 00:42:31.290 It is right here. 00:42:31.290 --> 00:42:34.779 So right here. 00:42:34.779 --> 00:42:35.820 Draw our transition rect. 00:42:35.820 --> 00:42:39.000 It's going to be drawn last so that it draws over everything when we finally 00:42:39.000 --> 00:42:42.540 do get a transition, but self.transitionAlpha, 00:42:42.540 --> 00:42:44.040 and that's all we really need. 00:42:44.040 --> 00:42:45.999 We need to keep the variable, and then whenever 00:42:45.999 --> 00:42:48.373 we want to perform like some sort of operation over time, 00:42:48.373 --> 00:42:49.480 just use timer.tween. 00:42:49.480 --> 00:42:51.510 It's that easy. 00:42:51.510 --> 00:42:52.920 But that was a little bit of a-- 00:42:52.920 --> 00:42:54.003 it was a relevant tangent. 00:42:54.003 --> 00:42:59.580 We would have talked about it anyway, but that's the first use case 00:42:59.580 --> 00:43:03.390 that I think of in this project, and then also the label. 00:43:03.390 --> 00:43:05.310 I'll show you the label in a little bit. 00:43:05.310 --> 00:43:08.760 But I think before we do that, let's talk about chaining. 00:43:08.760 --> 00:43:12.510 So you guys have probably played a lot of games 00:43:12.510 --> 00:43:15.302 where maybe there's a cut scene, and you're looking at a character, 00:43:15.302 --> 00:43:18.468 and they walk, and then maybe they turn, and they walk in another direction, 00:43:18.468 --> 00:43:20.440 and they walk up, and then they speak to you. 00:43:20.440 --> 00:43:23.940 There's a dialog box, and then maybe they do an animation or something, 00:43:23.940 --> 00:43:26.610 and then maybe some other things happen that are 00:43:26.610 --> 00:43:28.860 on some sort of predestined path. 00:43:28.860 --> 00:43:30.660 It's a very discrete path. 00:43:30.660 --> 00:43:32.259 It's not random. 00:43:32.259 --> 00:43:33.300 It's laid out in advance. 00:43:33.300 --> 00:43:37.320 It's a series of steps, one consecutive. 00:43:37.320 --> 00:43:42.120 That's the concept of chaining things together is relevant 00:43:42.120 --> 00:43:46.067 when we get to sort of timing things because when we finish timing 00:43:46.067 --> 00:43:48.150 something-- because usually, a lot of those things 00:43:48.150 --> 00:43:50.220 happen over the course of time. 00:43:50.220 --> 00:43:54.210 Over the course of five seconds, NPC1 will walk up north, 00:43:54.210 --> 00:43:58.934 and then they'll turn left, and then they'll say something. 00:43:58.934 --> 00:43:59.850 We want to model that. 00:43:59.850 --> 00:44:02.190 We don't want to basically have variables that say 00:44:02.190 --> 00:44:07.480 if NPC1 is at this tile, then do this. 00:44:07.480 --> 00:44:12.270 If NPC.dialogueOpen, then do this. 00:44:12.270 --> 00:44:15.970 We basically want to say walk here, do this, do this, 00:44:15.970 --> 00:44:24.780 do this in a flat, easy-- or at least semi-flat, easy sequence of steps. 00:44:24.780 --> 00:44:27.930 I have a few examples to illustrate how we can do that using timer 00:44:27.930 --> 00:44:30.810 for some semi-basic use cases. 00:44:30.810 --> 00:44:33.060 So chain0 is the first one. 00:44:33.060 --> 00:44:34.410 So this one is just Flappy Bird. 00:44:34.410 --> 00:44:38.700 He's going left to right, then he goes down, then he goes back left again, 00:44:38.700 --> 00:44:41.790 and then he goes up. 00:44:41.790 --> 00:44:46.570 What's the basic way that we model this-- 00:44:46.570 --> 00:44:49.680 that we implement this? 00:44:49.680 --> 00:44:50.430 Just off the cuff. 00:44:55.210 --> 00:44:58.080 AUDIENCE: We can use that finish thing to do the-- 00:44:58.080 --> 00:45:00.330 SPEAKER 1: We would. 00:45:00.330 --> 00:45:05.610 If we didn't know about finish, how would we probably do it? 00:45:05.610 --> 00:45:08.570 I shouldn't have given away finish before I got to that example. 00:45:08.570 --> 00:45:09.820 I kind of got ahead of myself. 00:45:12.640 --> 00:45:15.120 We can imagine somebody maybe saying OK, I 00:45:15.120 --> 00:45:19.110 want Flappy to move left to right, right to bottom, bottom to left, 00:45:19.110 --> 00:45:21.510 bottom to up. 00:45:21.510 --> 00:45:24.870 Maybe they're going to say if Flappy is less than-- 00:45:24.870 --> 00:45:31.680 or has reached first point, move left, else if he's reached bottom or point 2, 00:45:31.680 --> 00:45:34.500 move down, and then move left, move up. 00:45:34.500 --> 00:45:41.970 And in both of those cases, they're changing the x and y value of Flappy, 00:45:41.970 --> 00:45:45.870 and it's basically just a lot of ifs, and state variables. 00:45:48.410 --> 00:45:50.460 I see it in a surprising amount of code. 00:45:50.460 --> 00:45:54.090 Just state being kept all over the place. 00:45:54.090 --> 00:45:58.750 The first implementation of that that we'll look at uses something similar. 00:45:58.750 --> 00:46:05.250 So in chain0, and there's only two examples here, actually, for chain. 00:46:05.250 --> 00:46:13.500 But chain0, there's a movement time, and then a timer. 00:46:13.500 --> 00:46:15.420 We're going to be semi-clean about it. 00:46:15.420 --> 00:46:16.830 We have some destinations. 00:46:16.830 --> 00:46:18.600 OK, so we have destination1. 00:46:18.600 --> 00:46:25.020 I know that I don't necessarily want to keep track of a bunch of if statements, 00:46:25.020 --> 00:46:26.940 but I'm going for-- 00:46:26.940 --> 00:46:30.930 assuming that I don't know what timer can do for us, 00:46:30.930 --> 00:46:34.380 here, I'm just saying OK, I want this first destination 00:46:34.380 --> 00:46:38.310 to be virtual width minus his width, and then 00:46:38.310 --> 00:46:43.140 keep him at y0 So right edge of the screen, assuming that he starts at 0,0. 00:46:43.140 --> 00:46:46.650 And then I want his second destination to be that same side on the x-axis, 00:46:46.650 --> 00:46:49.660 but I want the y to be virtual height minus his height. 00:46:49.660 --> 00:46:51.390 So go to the bottom of the screen. 00:46:51.390 --> 00:46:54.740 Then I want it to be 0 in his height from the bottom of the screen, 00:46:54.740 --> 00:46:56.550 and then back to 0,0. 00:46:56.550 --> 00:47:01.740 So we have those modeled, and then I want to keep a flag in each of those. 00:47:01.740 --> 00:47:04.060 I want to know whether he's reached that state yet. 00:47:04.060 --> 00:47:07.080 So I'm going to iterate over that table I just created, 00:47:07.080 --> 00:47:10.149 and just add a new key to each of these called reached, 00:47:10.149 --> 00:47:11.190 and just set it to false. 00:47:11.190 --> 00:47:14.670 Just by default, he hasn't reached all of them yet. 00:47:14.670 --> 00:47:17.520 And then in the update, basically, I'm going 00:47:17.520 --> 00:47:20.730 to set a timer to the min of movement time. 00:47:20.730 --> 00:47:26.760 So it will never go higher than movement time, and then timer plus delta time. 00:47:26.760 --> 00:47:32.260 And then for every destination in destinations, if it wasn't reached, 00:47:32.260 --> 00:47:36.280 then set its x and y, FlappyX and FlappyY, which are, in this case, 00:47:36.280 --> 00:47:40.170 we're uncleanly using global variables to keep track of this. 00:47:40.170 --> 00:47:43.140 FlappyX and FlappyY gets baseX. 00:47:43.140 --> 00:47:44.340 So notice another problem. 00:47:44.340 --> 00:47:49.890 We have to maintain where we are relative to our next spot in order 00:47:49.890 --> 00:47:57.420 for this math to work because before, we just took Flappy Birds-- 00:47:57.420 --> 00:47:59.670 basically, the timer divided by movement time 00:47:59.670 --> 00:48:02.610 was a ratio where we scaled the end destination, 00:48:02.610 --> 00:48:06.240 and assigned that to Flappy, which had the effect of moving 00:48:06.240 --> 00:48:07.470 Flappy left to right. 00:48:07.470 --> 00:48:10.540 But if we do that in the opposite, right to left, 00:48:10.540 --> 00:48:14.250 the math isn't the same because he's going backwards. 00:48:14.250 --> 00:48:19.650 He's getting negative values added to his x value. 00:48:19.650 --> 00:48:22.560 So we need to keep track of a base that he started at for each 00:48:22.560 --> 00:48:25.620 of these operations baseX, baseY. 00:48:25.620 --> 00:48:28.629 So at the very beginning, baseX, baseY is 0,0. 00:48:28.629 --> 00:48:30.420 So it's actually going to be much the same, 00:48:30.420 --> 00:48:32.836 but as soon as Flappy get to the right edge of the screen, 00:48:32.836 --> 00:48:36.300 we want baseX to be the right edge of the screen, baseY still 0, 00:48:36.300 --> 00:48:40.230 and then if he goes down, we want baseY to then be bottom edge of the screen, 00:48:40.230 --> 00:48:42.820 baseX to be right edge, and so forth. 00:48:42.820 --> 00:48:45.390 So what we do is we just scale. 00:48:45.390 --> 00:48:50.070 We're still using a timer over movement time as our scale factor, 00:48:50.070 --> 00:48:54.930 but we're adding the difference of our destination and our base, 00:48:54.930 --> 00:48:58.990 and we're multiplying by that scale factor instead. 00:48:58.990 --> 00:49:01.740 And so this difference, if we add it, whether we're 00:49:01.740 --> 00:49:05.190 moving left, or right, or down, or up, it's 00:49:05.190 --> 00:49:09.060 going to have the effect of filling in that gap of bridging that no matter 00:49:09.060 --> 00:49:11.840 where we are, no matter which direction we want to go. 00:49:11.840 --> 00:49:18.600 And so this is basically a fairly complete linear interpolation 00:49:18.600 --> 00:49:21.630 algorithm, which is the basis of tweening. 00:49:21.630 --> 00:49:26.010 Just interpolate some value between another value. 00:49:26.010 --> 00:49:29.085 It's usually modeled in geometry as the line between two segments. 00:49:32.070 --> 00:49:35.510 And then if timer gets movement time, we've 00:49:35.510 --> 00:49:40.500 reached our destination, reset the timer, reset or baseX and Y, 00:49:40.500 --> 00:49:43.980 and that has the effect of just doing what we saw earlier, which was just 00:49:43.980 --> 00:49:47.290 putting him point by point. 00:49:47.290 --> 00:49:50.910 So any questions as to how this interpolation-- how 00:49:50.910 --> 00:49:53.100 this way of modeling the problem works? 00:49:57.230 --> 00:50:06.360 All right, so there is a better way, a much better way thanks to timer.finish, 00:50:06.360 --> 00:50:11.250 which you can apply to any timer operation, including timer.tween. 00:50:11.250 --> 00:50:17.730 So we can basically say OK, once that operation is finished, do something. 00:50:17.730 --> 00:50:21.014 And this is all we have to do, we just have to say timer.tween. 00:50:21.014 --> 00:50:22.680 We no longer have to interpolate at all. 00:50:22.680 --> 00:50:24.780 That's taken care of for us by timer. 00:50:24.780 --> 00:50:29.970 So we're doing timer.tween over movement time. 00:50:29.970 --> 00:50:34.680 Flappy, set it to-- this was before we add all this in a destinations table 00:50:34.680 --> 00:50:36.490 with reached flags as well. 00:50:36.490 --> 00:50:40.710 Now, we just have the x and y here. 00:50:40.710 --> 00:50:45.030 So on the first movement, we want his x to be right edge of the screen, 00:50:45.030 --> 00:50:46.890 just like before-- y get zero. 00:50:46.890 --> 00:50:52.710 Once that's finished, anonymous function with another timer.tween. 00:50:52.710 --> 00:50:54.520 So we're saying OK, once you're finished, 00:50:54.520 --> 00:50:59.100 then tween him from the top right edge to the bottom right edge. 00:50:59.100 --> 00:51:03.000 So y gets VIRTUAL_HEIGHT minus FlappySprite getHeight. 00:51:03.000 --> 00:51:06.470 And then once that's finished, another anonymous function, 00:51:06.470 --> 00:51:09.750 another timer.tween, another finish, another anonymous function, 00:51:09.750 --> 00:51:10.920 another timer.tween. 00:51:10.920 --> 00:51:14.010 And this is, in its own way, unscalable. 00:51:14.010 --> 00:51:15.480 It's nested. 00:51:15.480 --> 00:51:17.400 There's a term for it called call back hell 00:51:17.400 --> 00:51:22.440 because you just get infinite downwards sloping anonymous functions with all 00:51:22.440 --> 00:51:23.820 this behavior. 00:51:23.820 --> 00:51:27.740 There are ways to flatten it, and we potentially will talk about it. 00:51:27.740 --> 00:51:29.500 It's part of knife.chain. 00:51:29.500 --> 00:51:32.355 Knife.chain has a way to turn all of these-- 00:51:32.355 --> 00:51:34.230 basically, it would look something like this. 00:51:34.230 --> 00:51:38.940 It would be chain, and then it would be like moveFlappy x, y. 00:51:38.940 --> 00:51:43.230 MoveFlappy x2-- it wouldn't be x2, y2. 00:51:43.230 --> 00:51:49.340 We'd actually write these out here, but it would have the exact same effect. 00:51:49.340 --> 00:51:55.800 This is if you're looking to maybe implement a cut scene system, or just 00:51:55.800 --> 00:51:57.960 some sort of scripting system for your game 00:51:57.960 --> 00:52:03.840 that's very declarative, and imperative in style. 00:52:03.840 --> 00:52:10.710 This is the holy grail of changing behavior, and getting it to work, 00:52:10.710 --> 00:52:12.790 and just making it look nice, and readable. 00:52:12.790 --> 00:52:13.290 Yeah? 00:52:13.290 --> 00:52:16.713 AUDIENCE: Well, you could also pass in-- 00:52:16.713 --> 00:52:21.603 you could pass in a table to your function of x's and y's [INAUDIBLE] 00:52:21.603 --> 00:52:25.040 simpler for [INAUDIBLE] to chain a lot of things. 00:52:25.040 --> 00:52:27.190 SPEAKER 1: Yes, you could do that too. 00:52:27.190 --> 00:52:30.980 The response was you could pass in a table to-- 00:52:30.980 --> 00:52:33.350 you could iterate over a table, and within that table, 00:52:33.350 --> 00:52:36.110 generate a timer.tween. 00:52:36.110 --> 00:52:45.380 The only issue comes about with finish, and there is a way-- 00:52:45.380 --> 00:52:49.130 I guess you could get a reference back to the timer, 00:52:49.130 --> 00:52:55.770 and then add a finish block to it, but then you would lose out on-- 00:52:55.770 --> 00:53:03.200 that does work for the same function if all you're doing 00:53:03.200 --> 00:53:07.200 is moving something to a bunch of locations, it's absolutely true. 00:53:07.200 --> 00:53:19.130 But if we wanted Flappy say something, and hero disappear, and then 00:53:19.130 --> 00:53:23.102 hero flash, it gets a little bit trickier to do stuff like that. 00:53:23.102 --> 00:53:23.810 But yes, I agree. 00:53:23.810 --> 00:53:28.190 There are ways of modeling-- this particular example is a little bit 00:53:28.190 --> 00:53:31.160 repetitive, and could be modeled, I think, better 00:53:31.160 --> 00:53:34.550 with a function that takes advantage of the fact 00:53:34.550 --> 00:53:39.847 that timers can be returned, and then given new finish variables. 00:53:39.847 --> 00:53:42.430 I'd have to experiment with it to see because I'm actually not 00:53:42.430 --> 00:53:46.620 100% sure that you can add a finish. 00:53:46.620 --> 00:53:47.870 No, I think you can, actually. 00:53:47.870 --> 00:53:50.591 I think you can add a finish function to a reference 00:53:50.591 --> 00:53:52.340 because it's just a function on an object. 00:53:52.340 --> 00:53:58.670 So yeah, but independent of that, I think the goal probably 00:53:58.670 --> 00:54:04.640 is one, knowing how we can now chain behavior, and then two, 00:54:04.640 --> 00:54:06.680 striving towards flattening it. 00:54:06.680 --> 00:54:10.880 But in the purpose of this problem set, we'll see this a couple of times, 00:54:10.880 --> 00:54:14.780 and it's just worlds better than before. 00:54:14.780 --> 00:54:16.310 What's this, 76 lines? 00:54:16.310 --> 00:54:22.490 And then tween1 or chain0 was 96. 00:54:22.490 --> 00:54:25.490 OK, so 20 lines of code. 00:54:25.490 --> 00:54:29.032 And also the fact that now we have a declarative interface for modeling 00:54:29.032 --> 00:54:31.490 asynchronous behavior, that's really the fundamental thing, 00:54:31.490 --> 00:54:37.460 is not having some value that models your duration, or your counter, 00:54:37.460 --> 00:54:40.984 or whatever value, but just saying, hey, over this length of time, 00:54:40.984 --> 00:54:41.900 I want you to do this. 00:54:41.900 --> 00:54:42.660 I want you to do this. 00:54:42.660 --> 00:54:43.350 I want to do this. 00:54:43.350 --> 00:54:43.880 I want you to do this. 00:54:43.880 --> 00:54:45.921 After this length of time, I want you to do this. 00:54:49.910 --> 00:54:53.309 I always like to try and strive towards making code as declarative as possible 00:54:53.309 --> 00:54:55.100 just so that you can read it in the future, 00:54:55.100 --> 00:54:58.130 and then the people working on your code base 00:54:58.130 --> 00:55:01.040 can also read it well in the future, and I 00:55:01.040 --> 00:55:03.114 think timer does a pretty awesome job of that. 00:55:03.114 --> 00:55:05.030 And this is just a reference for timer finish. 00:55:05.030 --> 00:55:08.510 So it just takes a callback, and then once a timer function 00:55:08.510 --> 00:55:13.760 tween every or after has completed, it triggers that callback. 00:55:13.760 --> 00:55:17.340 So we're going to take a break for five minutes now, and then once we get back, 00:55:17.340 --> 00:55:21.740 we'll talk about swapping, and some of the algorithms we use in Match 3, 00:55:21.740 --> 00:55:25.490 starting with just rendering a board, getting tiles to swap, tweening them, 00:55:25.490 --> 00:55:29.150 and then we'll actually look at how we take falling tiles, 00:55:29.150 --> 00:55:31.210 and account for them, and then repopulate them. 00:55:36.080 --> 00:55:37.430 All right, we're back. 00:55:37.430 --> 00:55:41.930 So recall, before break we were looking at timer, 00:55:41.930 --> 00:55:47.090 and how to take code that was previously managed by timers, and asynchronous, 00:55:47.090 --> 00:55:50.720 but also very stateful, and sort of messy, and all over the place, 00:55:50.720 --> 00:55:53.160 and putting it into a more declarative, clean, 00:55:53.160 --> 00:55:59.290 easy to express format via timer.tween, timer.every, timer.after, timer.finish, 00:55:59.290 --> 00:56:02.360 timer;finish for any timer objects. 00:56:02.360 --> 00:56:04.370 So with all that out of the way, now we'll 00:56:04.370 --> 00:56:07.010 start talking about the actual Match 3 mechanics. 00:56:07.010 --> 00:56:11.000 And the very first thing that we'll look at is swap0, 00:56:11.000 --> 00:56:15.110 and this is the sprite sheet for Match 3 that's included in the distro. 00:56:15.110 --> 00:56:20.930 So as we can see, it's something that we can easily chop up with generate quads, 00:56:20.930 --> 00:56:21.710 as we saw before. 00:56:21.710 --> 00:56:24.080 Just a function provided in util.lua. 00:56:24.080 --> 00:56:25.647 These are all 32 by 32 pixels. 00:56:25.647 --> 00:56:27.980 So it would be very easy just to go through all of them, 00:56:27.980 --> 00:56:32.450 and basically just generate quads with the sprite sheet 32, 32, 00:56:32.450 --> 00:56:36.310 and just get a table with all of these individual things. 00:56:36.310 --> 00:56:41.960 But notice that they're blocked up into patterns of colors, 00:56:41.960 --> 00:56:44.870 and this has actual meaning, and value for our game 00:56:44.870 --> 00:56:47.990 because when something is the same color, 00:56:47.990 --> 00:56:51.800 and only when something is the same color, a tile is the same color, 00:56:51.800 --> 00:56:54.110 are we allowed to trigger a match with it. 00:56:54.110 --> 00:56:56.120 If we get any three or four-- 00:56:56.120 --> 00:56:59.450 anything higher than three together in a line, vertically 00:56:59.450 --> 00:57:04.070 or horizontally, that's a match, and we need to remove it from the table. 00:57:04.070 --> 00:57:07.580 So we need some sort of way of identifying these tiles as being 00:57:07.580 --> 00:57:11.990 of some color, and then they also happen to have a different pattern on them. 00:57:11.990 --> 00:57:15.470 This one's got nothing, but then an x, and then a circle, and a square. 00:57:15.470 --> 00:57:18.316 So those patterns-- it's not implemented in the distro, 00:57:18.316 --> 00:57:19.440 but part of the assignment. 00:57:19.440 --> 00:57:22.070 It's actually going to make them relevant. 00:57:22.070 --> 00:57:27.650 But the part that is implemented is the actual matching of the colors. 00:57:27.650 --> 00:57:31.220 And so the first thing that we'll need to do, probably, when we actually 00:57:31.220 --> 00:57:34.730 get into the core code of the distribution 00:57:34.730 --> 00:57:40.010 is instead of just putting them into one table, categorizing them. 00:57:40.010 --> 00:57:44.105 Splitting it up into maybe one, two, three, four, five, six, seven, eight, 00:57:44.105 --> 00:57:45.590 nine times two-- 00:57:45.590 --> 00:57:50.810 18 tables, so that we can just say gframes at color. 00:57:50.810 --> 00:57:54.440 So color being one to 18, and then get the index into that. 00:57:54.440 --> 00:57:56.310 So there's six within each one. 00:57:56.310 --> 00:57:57.620 So it'll be one to six. 00:57:57.620 --> 00:58:01.577 One to six will be the variety, with one being the base variety. 00:58:01.577 --> 00:58:03.410 And part of the assignment will be make sure 00:58:03.410 --> 00:58:08.600 that the base variety is the only one that we start with on level one, 00:58:08.600 --> 00:58:11.450 but then gradually introduce these other varieties. 00:58:11.450 --> 00:58:14.330 And you can put them in whatever hierarchy 00:58:14.330 --> 00:58:17.450 you want to, but make them have some sort of value 00:58:17.450 --> 00:58:20.812 later on top of a few other things the assignment will cover. 00:58:20.812 --> 00:58:22.520 But that's the spreadsheet in a nutshell, 00:58:22.520 --> 00:58:23.978 and we'll be splitting it that way. 00:58:23.978 --> 00:58:29.180 So 18 tables of quads, instead of one quad of whatever 00:58:29.180 --> 00:58:31.280 this amount is-- eight by 16. 00:58:31.280 --> 00:58:33.970 Not sure how many that is off the top of my head. 00:58:33.970 --> 00:58:38.790 But let's take a look at swap0 in the distribution 00:58:38.790 --> 00:58:41.840 so we can get a sense of what we need to do 00:58:41.840 --> 00:58:46.800 to begin implementing our Match 3 game. 00:58:46.800 --> 00:58:49.460 Notice we have require util for GenerateQuads. 00:58:49.460 --> 00:58:51.650 We're just going to generate for this basic example. 00:58:51.650 --> 00:58:54.816 We're not going to differentiate between colors, and varieties, or anything. 00:58:54.816 --> 00:58:58.127 We're just going to put them all into one quad, or one table of quads. 00:58:58.127 --> 00:59:00.710 So we're just going to use the regular GenerateQuads function. 00:59:00.710 --> 00:59:03.070 We're not going to differentiate them. 00:59:03.070 --> 00:59:05.400 We're just going to use our sprite here, our match3.png 00:59:05.400 --> 00:59:08.150 provided in the distro, which is the exact same image that we just 00:59:08.150 --> 00:59:09.470 saw on the screen. 00:59:09.470 --> 00:59:11.290 I'm going to just generate them. 00:59:11.290 --> 00:59:12.384 They're 32 by 32-- 00:59:12.384 --> 00:59:13.550 generate the quads for them. 00:59:13.550 --> 00:59:15.320 They're 32 by 32 pixels. 00:59:15.320 --> 00:59:19.310 I'm assigning it to a table here called TileQuads. 00:59:19.310 --> 00:59:21.830 And then here, we're calling generateBoard, 00:59:21.830 --> 00:59:25.700 and so generateBoard isn't all that dissimilar to what we saw before 00:59:25.700 --> 00:59:30.830 with maybe the level maker in Breakout, where we just spawn a bunch of bricks, 00:59:30.830 --> 00:59:33.440 or a bunch of tiles, in this case. 00:59:33.440 --> 00:59:37.220 Except in this case, they're kept in a nice 2D array that's 00:59:37.220 --> 00:59:39.860 always going to be eight by eight, and that's never 00:59:39.860 --> 00:59:43.437 going to change just by one of the constraints of the game. 00:59:43.437 --> 00:59:45.770 Match 3, traditionally, is an eight by eight grid that's 00:59:45.770 --> 00:59:48.540 always full of tiles of some variety. 00:59:48.540 --> 00:59:51.680 So local tiles, it's going to be an empty table, 00:59:51.680 --> 00:59:53.980 and then we're going to do a nested for loop here-- 00:59:53.980 --> 00:59:54.980 y to x. 00:59:54.980 --> 00:59:58.520 The standard is usually y first, and then x, 00:59:58.520 --> 01:00:05.480 and then we index y before x just because the individual rows in a 2D 01:00:05.480 --> 01:00:13.370 array, or sprite, or table will be such that, for example, if our table was 01:00:13.370 --> 01:00:15.020 equal to this-- oops, sorry. 01:00:15.020 --> 01:00:18.560 Syntax bug. 01:00:18.560 --> 01:00:25.040 If we did this, and then we have a table here, and a table here, and a table 01:00:25.040 --> 01:00:35.070 here, and we had like 0, 1, 1, 1, 1, 1, 1, 1, and then 1, 1, 1, 1. 01:00:35.070 --> 01:00:37.210 If we index into this table-- 01:00:37.210 --> 01:00:43.410 so table at 0, that's going to give us another table. 01:00:43.410 --> 01:00:45.060 That's not going to give us-- 01:00:45.060 --> 01:00:56.040 let's say we wanted to get the value at table 2, 3. 01:00:56.040 --> 01:01:00.990 The instinct might be to think I'm going to index at x, y 01:01:00.990 --> 01:01:04.350 just because x, y tends to be more commonly seen. 01:01:04.350 --> 01:01:08.100 But if we did that, and assumed that x was horizontal, 01:01:08.100 --> 01:01:13.080 and y is vertical in this arrangement, which is how we have it, 01:01:13.080 --> 01:01:20.350 then table2 wouldn't be this value here table2 would be this table here. 01:01:20.350 --> 01:01:26.700 The first value you passed into indexing some table when it's a nested table 01:01:26.700 --> 01:01:29.100 is just, in fact, the table itself, which 01:01:29.100 --> 01:01:33.300 is why we actually need to do-- when we want to get some value, 01:01:33.300 --> 01:01:35.850 we have to index at table y, x. 01:01:35.850 --> 01:01:40.395 So it's flipped for that reason because x is actually going to be-- 01:01:40.395 --> 01:01:46.680 the first index is going to be these sub tables, 01:01:46.680 --> 01:01:51.309 or sub arrays if you're in C, or Java, or something like that. 01:01:51.309 --> 01:01:54.225 So when you see table y, x, and you're wondering why it's not table x, 01:01:54.225 --> 01:01:56.790 y, that's the reason. 01:01:56.790 --> 01:02:01.700 So any questions as to why that is, or how that works? 01:02:01.700 --> 01:02:05.810 AUDIENCE: [INAUDIBLE] 01:02:05.810 --> 01:02:10.400 SPEAKER 1: In this case, I was using zero based indexing, 01:02:10.400 --> 01:02:11.802 but Lua is one index. 01:02:11.802 --> 01:02:12.635 That was just habit. 01:02:15.434 --> 01:02:18.350 It was pointed out that I was using zero based indexing in my example. 01:02:18.350 --> 01:02:22.940 You want to use one based indexing when you're actually programming, 01:02:22.940 --> 01:02:25.550 and not zero based. 01:02:25.550 --> 01:02:27.350 But the same principle applies. 01:02:27.350 --> 01:02:31.310 Zero would be one in that case. 01:02:31.310 --> 01:02:34.490 In a general purpose language-- 01:02:34.490 --> 01:02:42.500 most languages, if we were to abstract the problem out in a C 2D array, 01:02:42.500 --> 01:02:45.250 or C++, or Java, zero would be appropriate there. 01:02:45.250 --> 01:02:49.370 But anyways, we have a nested for loop. 01:02:49.370 --> 01:02:51.870 We're starting at y, and then we're going to x, 01:02:51.870 --> 01:02:55.270 and then basically, that has the effect of y, 01:02:55.270 --> 01:02:59.150 and then x, x, x, x, x, y, x, x, x, x, x. 01:02:59.150 --> 01:03:00.380 Just insert a blank table. 01:03:00.380 --> 01:03:05.300 Fill it with-- we're just using tables here. 01:03:05.300 --> 01:03:07.850 So we're not using any sort of tile class, 01:03:07.850 --> 01:03:10.190 or board class, or anything fancy. 01:03:10.190 --> 01:03:11.990 We're just using raw data types here. 01:03:11.990 --> 01:03:16.760 So we're just saying insert into tiles y, which 01:03:16.760 --> 01:03:22.670 by the way, if we're at x equals 1, 8, here, 01:03:22.670 --> 01:03:27.170 and we're in any given iteration of our outer y loop, 01:03:27.170 --> 01:03:30.640 tiles y will be the last table that we just inserted-- 01:03:30.640 --> 01:03:35.630 the last blank table on the first iteration of this x loop. 01:03:35.630 --> 01:03:39.710 So basically, it's saying, in the inner table that I just 01:03:39.710 --> 01:03:47.810 put into the table that's going to represent our board, the tiles table, 01:03:47.810 --> 01:03:51.650 insert a new table. 01:03:51.650 --> 01:03:55.820 So we have a table of tables of tables. 01:03:55.820 --> 01:03:59.150 In the third table are the actual tiles themselves. 01:03:59.150 --> 01:04:02.480 You can think of this table as being a tile data type, more or less. 01:04:02.480 --> 01:04:04.430 Just implemented using a table. 01:04:04.430 --> 01:04:07.610 That has an x-coordinate, a y-coordinate, and then a tile, 01:04:07.610 --> 01:04:11.000 and the tile is going to be a random number that's 01:04:11.000 --> 01:04:13.280 going to be the index into our quads. 01:04:13.280 --> 01:04:17.030 So each tile holds x and y, and then notice 01:04:17.030 --> 01:04:18.950 that we're multiplying by 32 because we're 01:04:18.950 --> 01:04:23.780 going to use this to draw the tile, and the tiles are 32 pixels tall by high-- 01:04:23.780 --> 01:04:26.340 sorry, wide by tall. 01:04:26.340 --> 01:04:29.300 We are going to multiply x minus 1, recall, 01:04:29.300 --> 01:04:35.510 because even though tables are one indexed, coordinates are zero indexed. 01:04:35.510 --> 01:04:39.320 So x gets that times 32, y gets that times 32, 01:04:39.320 --> 01:04:44.600 and then get a random number between 1, and the number of quads 01:04:44.600 --> 01:04:46.040 in our tile quads table. 01:04:46.040 --> 01:04:51.264 So recall this number sign here is just a shorthand for length of the table, 01:04:51.264 --> 01:04:52.680 and then we're going to return it. 01:04:52.680 --> 01:04:54.290 So we generated our board. 01:04:54.290 --> 01:05:01.660 It's a y by x grid of table rows of little tables 01:05:01.660 --> 01:05:05.450 that all have an x, y, and a tile ID, and the tile ID maps to the quads 01:05:05.450 --> 01:05:09.350 that we just generated. 01:05:09.350 --> 01:05:11.630 OK, that's it for the init function. 01:05:11.630 --> 01:05:14.450 Sorry, love.load in this example. 01:05:14.450 --> 01:05:20.620 The love.draw uses a function called drawBoard, and we pass in 128 by 16. 01:05:20.620 --> 01:05:23.330 The 128 by 16 is just xy offsets. 01:05:23.330 --> 01:05:25.640 We're just going to draw our board at 128, 16, 01:05:25.640 --> 01:05:28.460 and is it going to center our board. 01:05:28.460 --> 01:05:31.190 And then if we go down to drawBoard at line 89, 01:05:31.190 --> 01:05:34.130 gets an offsetX, offsetY, nested for loop again. 01:05:34.130 --> 01:05:37.520 We're just iterating back over the tiles that we got, 01:05:37.520 --> 01:05:40.820 and recall, actually, generateBoard returns tiles, 01:05:40.820 --> 01:05:45.350 and then we set board equal to the result of generateBoard. 01:05:45.350 --> 01:05:50.210 So down here in line 89, again, in drawBoard-- actually at line 95. 01:05:50.210 --> 01:05:56.255 Within this nested for loop, we're going to get a tile at board y, x, 01:05:56.255 --> 01:05:58.130 just so we have a shorthand reference for it. 01:05:58.130 --> 01:06:01.760 We don't have to say board y, x several times, which you would have to hear. 01:06:01.760 --> 01:06:04.040 We're going to draw the sprite-- 01:06:04.040 --> 01:06:07.319 the quad at tile, tile, tile.tile, which, recall, 01:06:07.319 --> 01:06:09.110 is a random number between one and whatever 01:06:09.110 --> 01:06:13.170 the number of quads we have in our tile quads table. 01:06:13.170 --> 01:06:16.940 And then that x plus offsetX, and the y plus the offsetY, which 01:06:16.940 --> 01:06:22.040 has the effect of drawing every single tile in our grid at some given offset. 01:06:22.040 --> 01:06:23.180 And that has the result-- 01:06:23.180 --> 01:06:26.600 and I probably should have run this, actually, in advance 01:06:26.600 --> 01:06:28.950 just so I could illustrate it. 01:06:31.800 --> 01:06:34.340 But we have a simple board. 01:06:34.340 --> 01:06:35.080 Looks nice. 01:06:35.080 --> 01:06:37.580 It's colorful, but it's very, very basic. 01:06:37.580 --> 01:06:39.697 Just a 2D render of our game. 01:06:39.697 --> 01:06:41.030 There's no behavior or anything. 01:06:41.030 --> 01:06:43.070 This is just how we draw the board. 01:06:43.070 --> 01:06:46.510 So any questions as to how just the drawing, and the creation of the board 01:06:46.510 --> 01:06:47.010 work? 01:06:50.340 --> 01:06:51.890 OK. 01:06:51.890 --> 01:06:56.010 So swap1 is a little bit more complicated. 01:06:56.010 --> 01:06:58.820 It builds on what we did before, what we just 01:06:58.820 --> 01:07:02.569 built, which was getting the board implemented, and drawn onto the screen. 01:07:02.569 --> 01:07:03.860 But there's no behavior at all. 01:07:03.860 --> 01:07:05.870 It's just a static-- basically, the same thing 01:07:05.870 --> 01:07:07.640 as drawing an image onto the screen. 01:07:07.640 --> 01:07:13.070 And so for that, what we want to do is implement swapping. 01:07:13.070 --> 01:07:17.810 So how do we think we can accomplish this? 01:07:17.810 --> 01:07:20.660 Anybody have any ideas as to how we can swap? 01:07:23.618 --> 01:07:27.570 AUDIENCE: [INAUDIBLE] using tweening to have it go in opposite directions. 01:07:27.570 --> 01:07:30.060 SPEAKER 1: Well, we could. 01:07:30.060 --> 01:07:32.970 That will have the effect of-- 01:07:32.970 --> 01:07:34.890 the response was we could use tweeting. 01:07:34.890 --> 01:07:38.790 We could, and we actually will for swap2, 01:07:38.790 --> 01:07:41.400 but it's going to be a little bit more complicated than that 01:07:41.400 --> 01:07:44.447 because they're in a 2D array. 01:07:44.447 --> 01:07:45.780 So if we just tween their x, y-- 01:07:45.780 --> 01:07:50.140 AUDIENCE: [INAUDIBLE] 01:07:50.140 --> 01:07:53.670 SPEAKER 1: They will be in the same place in the array. 01:07:53.670 --> 01:07:54.462 But yes, ultimate-- 01:07:54.462 --> 01:07:56.670 AUDIENCE: You could switch the position in the array, 01:07:56.670 --> 01:07:57.870 and then reload the array. 01:07:57.870 --> 01:08:00.330 SPEAKER 1: Switch their position in the array, and then reload the array. 01:08:00.330 --> 01:08:01.496 We will switch the position. 01:08:01.496 --> 01:08:04.679 We don't have to reload the array, but we will switch their positions. 01:08:04.679 --> 01:08:05.970 That's effectively, what it is. 01:08:05.970 --> 01:08:09.990 Literally just take two tiles, and swap in CS50, 01:08:09.990 --> 01:08:13.560 where we just take two variables, and get a temp variable that 01:08:13.560 --> 01:08:16.080 points that one variable, gets its values 01:08:16.080 --> 01:08:22.689 while the second variable gets the values of that one, or vise versa, 01:08:22.689 --> 01:08:23.189 I think. 01:08:23.189 --> 01:08:27.479 This one gets this one's values, this one gets this one's values, 01:08:27.479 --> 01:08:29.700 and then this one comes down to this one, basically. 01:08:29.700 --> 01:08:32.910 There's the middleman that keep-- because if this one gets this one's 01:08:32.910 --> 01:08:35.966 values, it's going to get overridden by this one's value. 01:08:35.966 --> 01:08:38.340 So there would be no reference to it's x, y, or anything. 01:08:38.340 --> 01:08:42.180 So that's why you need to store this one up here, so this one can come here, 01:08:42.180 --> 01:08:43.750 and this and come back down here. 01:08:43.750 --> 01:08:45.630 So we've done a swap, effectively. 01:08:45.630 --> 01:08:47.910 And there's ways in Lua to do swaps, as we saw before, 01:08:47.910 --> 01:08:49.659 without even needing a temporary variable. 01:08:49.659 --> 01:08:54.130 You can just do xy get some other xy, which sort of bypasses that. 01:08:54.130 --> 01:08:57.510 But when you start to do four things getting swapped at once, 01:08:57.510 --> 01:09:01.830 and you have four commas, it can get a little tricky, a little bit unwieldy. 01:09:01.830 --> 01:09:04.779 I'm actually not 100% sure you can unpack more than two things in Lua. 01:09:04.779 --> 01:09:09.060 I'll have to double check on that, but right off the gate, 01:09:09.060 --> 01:09:11.850 we're seeing that double assignment here on line 32, 01:09:11.850 --> 01:09:15.069 highlighted x, highlighted y gets 1, 1. 01:09:15.069 --> 01:09:17.069 And let me actually run just so we can see. 01:09:17.069 --> 01:09:20.460 There's actually a couple of pieces here besides just that. 01:09:24.569 --> 01:09:25.930 Swap1. 01:09:25.930 --> 01:09:29.279 So we have the board as before, we also have 01:09:29.279 --> 01:09:32.069 something to show us where to swipe because we have 01:09:32.069 --> 01:09:34.380 to know where we're swapping the tiles. 01:09:34.380 --> 01:09:39.057 In an ideal implementation, which is an optional part of the assignment, 01:09:39.057 --> 01:09:40.890 you would have mouse behavior for your game. 01:09:40.890 --> 01:09:43.899 So you could just click on two tiles, or click, and drag, and swap them. 01:09:43.899 --> 01:09:45.357 In this case, we're not doing that. 01:09:45.357 --> 01:09:47.430 We're just implementing key based behavior. 01:09:47.430 --> 01:09:50.760 So when I press left, right, up, or down, I can move. 01:09:50.760 --> 01:09:55.950 If I press Enter on a tile, and then move around, it's an indicator to me 01:09:55.950 --> 01:09:58.290 that I've selected that tile to swap with something else 01:09:58.290 --> 01:10:01.300 because it needs to keep track of OK, you want to swap this tile. 01:10:01.300 --> 01:10:02.675 What do you want to swap it with? 01:10:02.675 --> 01:10:04.850 I want to swap it with this tile. 01:10:04.850 --> 01:10:06.210 So they get swapped. 01:10:06.210 --> 01:10:08.910 I want to swap it with this tile. 01:10:08.910 --> 01:10:10.230 So they get swapped. 01:10:10.230 --> 01:10:11.387 Or this tile. 01:10:11.387 --> 01:10:13.470 So you can swap it with whatever tile you want to. 01:10:13.470 --> 01:10:16.020 There's no constraints. 01:10:16.020 --> 01:10:21.150 The actual distro code implements a constraint, and so offhand, 01:10:21.150 --> 01:10:24.342 what do you think a constraint would be for making sure that we can't-- 01:10:24.342 --> 01:10:25.800 AUDIENCE: They have to be adjacent. 01:10:25.800 --> 01:10:27.550 SPEAKER 1: Yeah, they have to be adjacent. 01:10:27.550 --> 01:10:31.020 So what would that entail? 01:10:31.020 --> 01:10:34.450 AUDIENCE: That their x [INAUDIBLE] 01:10:34.450 --> 01:10:35.950 SPEAKER 1: Exactly. 01:10:35.950 --> 01:10:41.880 And the shorthand for that, really, is if the absolute value of their x's 01:10:41.880 --> 01:10:48.910 minus their y's is equal to 1. 01:10:48.910 --> 01:10:52.320 Because if you subtract one's x from another one's x, 01:10:52.320 --> 01:10:54.510 and then one's y from another one's y, and then 01:10:54.510 --> 01:10:56.400 you add the differences together, that'll 01:10:56.400 --> 01:10:58.860 tell you whether they're directly adjacent to each other. 01:10:58.860 --> 01:10:59.950 It has to equal one. 01:10:59.950 --> 01:11:02.750 If equals zero, then their x's and y's are the same. 01:11:02.750 --> 01:11:07.350 If equals two, then it's two tiles away on the x-axis, 01:11:07.350 --> 01:11:13.660 or it's away on the x and the y, in which case it would be diagonal to it. 01:11:13.660 --> 01:11:16.780 So the only way is it's x's minus it's x's. 01:11:16.780 --> 01:11:21.210 Tile1.x minus tile2.x, and tile1.y minus tile2.y, 01:11:21.210 --> 01:11:24.840 if they're absolute value of their difference is one, 01:11:24.840 --> 01:11:26.100 then they're adjacent. 01:11:26.100 --> 01:11:27.846 That's in the implementation. 01:11:33.120 --> 01:11:36.305 So this is why we have these variables here, highlighted tile. 01:11:36.305 --> 01:11:39.180 Basically we're setting a flag saying, do we have a highlighted tile? 01:11:39.180 --> 01:11:45.090 If we do, we're going to perform some drawing logic later down in the draw 01:11:45.090 --> 01:11:46.620 function. 01:11:46.620 --> 01:11:52.554 Basically, how would we draw a highlighted tile, do you think? 01:11:52.554 --> 01:11:55.239 AUDIENCE: Add a rectangle with transparency. 01:11:55.239 --> 01:11:56.030 SPEAKER 1: Exactly. 01:11:56.030 --> 01:11:58.260 So the answer was add a rectangle with transparency. 01:11:58.260 --> 01:11:59.910 That's exactly what we do. 01:11:59.910 --> 01:12:07.050 I'm going to go down to this part here. 01:12:07.050 --> 01:12:10.900 So on line 173, if we have a highlighted tile, 01:12:10.900 --> 01:12:12.930 and basically, this is in the middle of a loop-- 01:12:12.930 --> 01:12:14.610 our y, x before. 01:12:14.610 --> 01:12:16.160 We've put it into a draw board. 01:12:16.160 --> 01:12:19.980 We have the drawBoard function, but x, y, or y, x, 01:12:19.980 --> 01:12:22.540 the tile is going to be whatever tile we're currently on, 01:12:22.540 --> 01:12:27.200 and if we do have a highlighted tile, and that tile's gridX-- 01:12:27.200 --> 01:12:31.019 notice we now have a new variable called gridX, as opposed to it's regular x 01:12:31.019 --> 01:12:34.310 so that we can check for these sorts of things to see where it is in the array. 01:12:34.310 --> 01:12:39.500 If it's gridX is equal to whatever we've set highlightedX to, 01:12:39.500 --> 01:12:45.400 and gridY is equal to highlightedY, then love.graphics.setColor 01:12:45.400 --> 01:12:50.000 half transparency, and then just draw a rectangle with this 4 01:12:50.000 --> 01:12:53.920 at the end of it, which actually draws a rounded rectangle. 01:12:53.920 --> 01:12:56.550 If you pass in no 4, it will just draw straight rectangle, 01:12:56.550 --> 01:12:58.970 but if you pass in an int at the very end, that's how many 01:12:58.970 --> 01:13:01.597 rounded segments basically that rectangle will have. 01:13:01.597 --> 01:13:03.680 So you can get rounded corners on your rectangles, 01:13:03.680 --> 01:13:07.040 and it's good for UI drawing, and stuff like that. 01:13:07.040 --> 01:13:10.910 We use it a little bit in the distro. 01:13:10.910 --> 01:13:13.010 So that's how you get a highlighted tile. 01:13:13.010 --> 01:13:15.740 There was also a selected tile, and a selected tile 01:13:15.740 --> 01:13:19.847 is just draw a rectangle, same thing, but it's a line this time, 01:13:19.847 --> 01:13:22.430 and there's always going to be a selected tile no matter what. 01:13:22.430 --> 01:13:25.430 So we're always going to draw it here at the end of our render function. 01:13:25.430 --> 01:13:26.410 It's just 255-- 01:13:26.410 --> 01:13:30.080 234 for the opacity so that it's just kind of transparent, but not super 01:13:30.080 --> 01:13:31.670 transparent. 01:13:31.670 --> 01:13:37.800 Set line width to 4 so that it's not just a very thin rectangle. 01:13:37.800 --> 01:13:44.990 If you set the line width, and then you draw a rectangle with the line format-- 01:13:44.990 --> 01:13:48.530 the line mode of drawing, it will use whatever the current line 01:13:48.530 --> 01:13:50.370 width is when drawing the rectangle. 01:13:50.370 --> 01:13:54.740 So we set it to 4, then draw a line rect at selectedTile.x 01:13:54.740 --> 01:13:59.039 plus offsetX selectedTile.y offsetY, and we draw it 32 by 32 01:13:59.039 --> 01:14:00.830 because that's the size of a tile, and then 01:14:00.830 --> 01:14:05.360 we set our color-- remember to always set your color back to 255, 255, 255, 01:14:05.360 --> 01:14:09.770 255 because if you don't, and I did this when I was debugging, actually, 01:14:09.770 --> 01:14:16.200 you get some fun stuff. 01:14:16.200 --> 01:14:18.172 Wait, was that the right one? 01:14:23.650 --> 01:14:27.240 Oh, I might have fixed it up above where we-- 01:14:27.240 --> 01:14:28.600 there was an issue. 01:14:28.600 --> 01:14:34.260 If you don't set, basically, your color, and you set it to red, 01:14:34.260 --> 01:14:36.840 everything will draw red after you've done something. 01:14:36.840 --> 01:14:42.640 So if it ever happens, remember to set your color back to 255, 255, 255, 255, 01:14:42.640 --> 01:14:46.503 anytime you change the color in some way, like I'm doing here. 01:14:46.503 --> 01:14:48.336 AUDIENCE: Alternatively, you could also just 01:14:48.336 --> 01:14:50.820 make sure to always set the color before you draw something. is that right? 01:14:50.820 --> 01:14:51.962 SPEAKER 1: Yes. 01:14:51.962 --> 01:14:53.670 The response was make sure you always set 01:14:53.670 --> 01:14:55.170 the color before you draw something. 01:14:55.170 --> 01:14:57.540 I think that's what I ended up doing in this distro, which 01:14:57.540 --> 01:14:58.873 is why it's not working anymore. 01:14:58.873 --> 01:14:59.880 I think it was-- 01:14:59.880 --> 01:15:01.870 where was it? 01:15:01.870 --> 01:15:05.130 It was here, but I must have fixed it because I accidentally 01:15:05.130 --> 01:15:08.130 left that out when I was debugging, and it ended up drawing-- everything 01:15:08.130 --> 01:15:08.629 was red. 01:15:08.629 --> 01:15:12.660 So just as an aside just because Love2D is a state machine. 01:15:12.660 --> 01:15:16.440 Drawing it beforehand is definitely the safer way to go too. 01:15:19.522 --> 01:15:22.230 So the core of this, because we're running a little low on time-- 01:15:22.230 --> 01:15:28.300 the core of this overall block of code is just the swap here. 01:15:28.300 --> 01:15:30.480 So if there is no highlighted tile-- so basically, 01:15:30.480 --> 01:15:32.250 if we pressed Enter or Return-- 01:15:32.250 --> 01:15:36.510 now, we have all input handling in love.keypressed key. 01:15:36.510 --> 01:15:38.910 And by the way, this is input handling to change 01:15:38.910 --> 01:15:40.500 the x and y of our selected tile. 01:15:43.680 --> 01:15:46.170 If we press Enter, and we don't have a highlighted tile, 01:15:46.170 --> 01:15:49.470 then we need to have a highlighted tile, otherwise we should swap them. 01:15:49.470 --> 01:15:53.724 So we get a reference to tile one and two, we swap, we create temp variables. 01:15:53.724 --> 01:15:55.890 Recall, we need to have that middle man up here that 01:15:55.890 --> 01:15:57.910 keeps track of this tile's information. 01:15:57.910 --> 01:16:00.285 So it's going to keep track of all of tile2's information 01:16:00.285 --> 01:16:03.480 with tempX, tempY, tempgridX, and tempgridY because we 01:16:03.480 --> 01:16:05.880 need to not only change their x-coordinates, 01:16:05.880 --> 01:16:08.190 but also their grid positions. 01:16:08.190 --> 01:16:16.110 And then we need to create a temp tile here. 01:16:16.110 --> 01:16:19.470 Basically, here's where we actually swap their places in the board. 01:16:19.470 --> 01:16:24.600 So tile1.gridY, tile1.gridX gets tile2, and then we're getting a reference 01:16:24.600 --> 01:16:27.090 to temp tile so that we can-- 01:16:27.090 --> 01:16:35.940 because if we set board at wherever tile1 is to tile2, 01:16:35.940 --> 01:16:37.890 we won't have anything where tile2 is. 01:16:37.890 --> 01:16:40.250 We need to have a temp tile to keep track of-- 01:16:40.250 --> 01:16:46.170 sorry, we won't have anything at tile1 if we overwrite it with tile2. 01:16:46.170 --> 01:16:49.410 So we need a reference to tile1 here, so that we can put it 01:16:49.410 --> 01:16:52.780 into where tile2's spot is, right here. 01:16:52.780 --> 01:16:58.486 And then we need to do all that before we end up swapping their coordinates 01:16:58.486 --> 01:17:01.110 and tile grid positions, otherwise you get weird buggy behavior 01:17:01.110 --> 01:17:03.880 when you're moving the selected tile around. 01:17:03.880 --> 01:17:06.960 And then we can on the highlight, and then reset our selection 01:17:06.960 --> 01:17:10.330 because their selection is also going to get changed after we do the swap. 01:17:10.330 --> 01:17:15.090 So we need to put it to the second tile because it gets swapped with whatever 01:17:15.090 --> 01:17:18.110 tile we highlighted. 01:17:18.110 --> 01:17:19.940 And that's the overall gist. 01:17:19.940 --> 01:17:22.830 It's basically taking two tiles, flipping the information, 01:17:22.830 --> 01:17:23.970 storing a middleman. 01:17:23.970 --> 01:17:26.260 Same thing in swap in CS50, a little more complicated, 01:17:26.260 --> 01:17:30.210 though because these all have subfields that all need to get manipulated. 01:17:30.210 --> 01:17:32.530 And a lot of this can actually be done mathematically. 01:17:32.530 --> 01:17:37.407 You can actually have its x and y mathematically derived 01:17:37.407 --> 01:17:38.490 from it's gridX and gridY. 01:17:38.490 --> 01:17:40.620 Just multiply by 32. 01:17:40.620 --> 01:17:43.920 In this case, I just kept them as variables, and separate. 01:17:43.920 --> 01:17:47.430 But yeah, you could just do that too, and that 01:17:47.430 --> 01:17:51.930 has the effect of swapping the variables whenever we move them, 01:17:51.930 --> 01:17:56.490 and then that's the fundamental first step in Match 3, 01:17:56.490 --> 01:17:58.900 is just swapping any two tiles in a given position. 01:17:58.900 --> 01:18:01.770 So does that make sense altogether? 01:18:01.770 --> 01:18:03.630 OK. 01:18:03.630 --> 01:18:11.070 So this example is actually not that much different at all from swap2. 01:18:11.070 --> 01:18:13.200 I'm going to show you swap 2 right now. 01:18:13.200 --> 01:18:19.530 So if we go to swap2, the only change we really have made 01:18:19.530 --> 01:18:26.090 is that now tiles flipped instead of instantly changing, they tween. 01:18:26.090 --> 01:18:27.840 And this is a piece of cake at this point. 01:18:27.840 --> 01:18:31.030 We already know-- what's the function we need to do? 01:18:31.030 --> 01:18:32.742 Just timer.tween. 01:18:32.742 --> 01:18:34.950 All we need to do is just take the two, and then just 01:18:34.950 --> 01:18:40.030 tween tile1.x and tile1.y to tile2.x, and tile2.y, 01:18:40.030 --> 01:18:41.700 and do the same thing in reverse. 01:18:41.700 --> 01:18:46.210 Tween tile2.x, and tile2.y to tile1.x, and tile1.y. 01:18:46.210 --> 01:18:52.740 And so if we open up swap2, go to main, nothing in this program 01:18:52.740 --> 01:18:58.050 really changes, except in update, where we go to line 99, 01:18:58.050 --> 01:18:59.294 and we're just doing it here. 01:18:59.294 --> 01:19:00.210 Notice the definition. 01:19:00.210 --> 01:19:03.479 Over 0.2 seconds, it takes in the definition table, here, 01:19:03.479 --> 01:19:06.270 and it's taking in two entities because we're modifying two things. 01:19:06.270 --> 01:19:08.640 We're modifying tile2, and tile1. 01:19:08.640 --> 01:19:13.230 We're just setting x to tile1.x, and y to tile1.y, 01:19:13.230 --> 01:19:15.967 and then tile1 is getting the tempX and tempY 01:19:15.967 --> 01:19:18.550 because before, it was just getting it directly from the temp, 01:19:18.550 --> 01:19:22.270 and now it's just tweening it over time. 01:19:22.270 --> 01:19:26.830 But that was before just a bunch of tile2.x equals tile1.x, 01:19:26.830 --> 01:19:33.310 tile2.y equals tile2.y, tile1.y equals tile2.y. 01:19:33.310 --> 01:19:34.070 That's all it is. 01:19:34.070 --> 01:19:35.528 That's what's really nice about it. 01:19:35.528 --> 01:19:38.050 Now we don't have to really work hard at all 01:19:38.050 --> 01:19:41.890 to get nice, smooth transitions in whatever we do, 01:19:41.890 --> 01:19:43.870 whether it's a UI, or the game. 01:19:43.870 --> 01:19:47.249 It's just super nice, and convenient. 01:19:47.249 --> 01:19:49.540 So that's all we need to do to get basic swapping done. 01:19:49.540 --> 01:19:51.840 That was swap2, the tween swap. 01:19:51.840 --> 01:19:54.070 And so I put together a set of slides here just 01:19:54.070 --> 01:19:58.180 to illustrate the algorithm that we use to calculate the matches. 01:19:58.180 --> 01:20:02.080 So right now we've got swapping in, but we 01:20:02.080 --> 01:20:03.880 don't know when we've gotten a match. 01:20:03.880 --> 01:20:06.850 So just offhand, does anybody have any idea as to maybe 01:20:06.850 --> 01:20:09.732 how we can go about calculating whether we've got any matches? 01:20:15.188 --> 01:20:18.660 AUDIENCE: Well, we already figured out how to track if a thing is adjacent. 01:20:18.660 --> 01:20:21.636 So you, I guess, have a table of adjacent-- 01:20:21.636 --> 01:20:24.612 you go through [INAUDIBLE] block, and if you have an adjacent-- 01:20:24.612 --> 01:20:29.076 or for every adjacent block, you check if that color equals your color. 01:20:29.076 --> 01:20:31.824 And if it does, you check if-- 01:20:31.824 --> 01:20:34.532 well, then I guess you need to figure out what direction it's in, 01:20:34.532 --> 01:20:37.508 and then you check, continuing in that direction, 01:20:37.508 --> 01:20:40.050 if there's another of the same color. 01:20:40.050 --> 01:20:42.840 SPEAKER 1: So their response was when you're 01:20:42.840 --> 01:20:46.920 looking at tiles, look at all adjacent tiles, 01:20:46.920 --> 01:20:52.470 and if there is a color that's the same one, then figure out its direction, 01:20:52.470 --> 01:20:55.130 and then move from there. 01:20:55.130 --> 01:20:56.950 So like a recursive style. 01:20:56.950 --> 01:20:59.934 I guess you could implement it recursively. 01:20:59.934 --> 01:21:02.850 It probably would be a little bit trickier to understand, and probably 01:21:02.850 --> 01:21:05.130 not as efficient. 01:21:05.130 --> 01:21:07.950 The way that we are actually going to implement it 01:21:07.950 --> 01:21:13.320 is going to be a little bit more iterative. 01:21:13.320 --> 01:21:19.290 So all we really need to do is check every row, and every column one time, 01:21:19.290 --> 01:21:21.450 and then go basically, left to right. 01:21:21.450 --> 01:21:25.412 So in this case, we have to check every row and column one 01:21:25.412 --> 01:21:27.870 time in this direction, and then one time in this direction 01:21:27.870 --> 01:21:33.420 because we can get vertical and horizontal matches. 01:21:33.420 --> 01:21:35.350 So we start off. 01:21:35.350 --> 01:21:40.450 Let's just arbitrarily decide we want to start going left to right down the data 01:21:40.450 --> 01:21:41.250 structure. 01:21:41.250 --> 01:21:43.950 So we'll go, what color is this? 01:21:43.950 --> 01:21:44.600 That's brown. 01:21:44.600 --> 01:21:46.200 OK, check the next one. 01:21:46.200 --> 01:21:47.820 Is it the same color? 01:21:47.820 --> 01:21:52.680 If it is, then say OK, the number of matching tiles that we've found so far 01:21:52.680 --> 01:21:54.090 is two. 01:21:54.090 --> 01:21:57.060 If it's greater than three, then later on we'll 01:21:57.060 --> 01:22:03.130 need to add that group as a match to our list of matches, basically. 01:22:03.130 --> 01:22:07.340 But if it's not, OK, then the number matches is one again. 01:22:07.340 --> 01:22:09.510 So set it to one, and then do the same thing. 01:22:09.510 --> 01:22:10.211 Same color? 01:22:10.211 --> 01:22:10.710 No. 01:22:10.710 --> 01:22:12.780 OK, number of matches is one. 01:22:12.780 --> 01:22:16.680 In this case, here we have the number of matches is going to be two 01:22:16.680 --> 01:22:19.770 because this is blue, and then we're going to go ahead, 01:22:19.770 --> 01:22:20.865 and then same color again. 01:22:20.865 --> 01:22:25.450 The number matches is three, and then we've gotten to the end of the row. 01:22:25.450 --> 01:22:28.680 So we can say OK, what was our last number of matches? 01:22:28.680 --> 01:22:31.050 Was it greater than or equal to three? 01:22:31.050 --> 01:22:34.080 If it was, add that group of tiles to our table of 01:22:34.080 --> 01:22:37.650 matches if we've gotten a match, and then move on. 01:22:37.650 --> 01:22:40.950 And we do that over, and over again, and if it's in the middle of a group, 01:22:40.950 --> 01:22:43.200 like it is here-- so this isn't at the end of the row. 01:22:43.200 --> 01:22:44.783 This is just in the middle of the row. 01:22:44.783 --> 01:22:49.170 What we do is number of matches one, two, three, and then we go here, 01:22:49.170 --> 01:22:50.434 and it's set to one. 01:22:50.434 --> 01:22:52.350 Well, first of all, we check number of matches 01:22:52.350 --> 01:22:53.724 when we get to a different color. 01:22:53.724 --> 01:22:56.180 We say, OK, this isn't the same color as this tile. 01:22:56.180 --> 01:23:00.960 This is purple, and this is gray, but number of matches is three. 01:23:00.960 --> 01:23:06.880 So what we do is we just add these three tiles to our-- 01:23:06.880 --> 01:23:09.960 we're keeping a table of matches because we're going to go through, 01:23:09.960 --> 01:23:11.550 and we're going to delete all of them. 01:23:11.550 --> 01:23:15.090 And then, eventually, we're going to do some tweening as well, 01:23:15.090 --> 01:23:17.100 but we're going to delete all of these. 01:23:17.100 --> 01:23:20.220 And then in order to do that, we need to walk backwards. 01:23:20.220 --> 01:23:24.240 We need to say, basically, for x gets position 01:23:24.240 --> 01:23:28.742 minus number of tiles in the match, just add that to a match, 01:23:28.742 --> 01:23:30.450 add that to a match, add that to a match, 01:23:30.450 --> 01:23:32.850 and then add the match to our table of matches. 01:23:32.850 --> 01:23:34.685 And that's it for the x direction. 01:23:34.685 --> 01:23:36.810 And for the y direction, it's the exact same thing. 01:23:36.810 --> 01:23:39.643 Going down here-- different color, different color, different color, 01:23:39.643 --> 01:23:42.490 different color, and then same color, same color, different color, 01:23:42.490 --> 01:23:46.470 but number of matches is three because one, two, three, and then 01:23:46.470 --> 01:23:50.290 it's going to walk backwards, up to the top, add that match, 01:23:50.290 --> 01:23:51.750 and then just continue down here. 01:23:51.750 --> 01:23:54.180 Same thing, same thing, and then same thing there. 01:23:54.180 --> 01:23:56.350 This is at the end of the column. 01:23:56.350 --> 01:23:57.690 So it's going to get to the end. 01:23:57.690 --> 01:24:00.900 It's not actually going to look for the next tile 01:24:00.900 --> 01:24:03.270 because there are no more tiles, but every time 01:24:03.270 --> 01:24:07.122 we complete a row, or a column, we check at the very end, 01:24:07.122 --> 01:24:09.580 do we have the number of matches equal to three or greater? 01:24:09.580 --> 01:24:11.520 If we do, then we need to do the same logic 01:24:11.520 --> 01:24:14.880 as we did before by adding that match to our list of matches. 01:24:14.880 --> 01:24:19.440 So it's actually quite a simple algorithm, and this is the set of steps 01:24:19.440 --> 01:24:21.630 that I just illustrated. 01:24:21.630 --> 01:24:23.295 We have a match found there. 01:24:23.295 --> 01:24:24.420 Oh, sorry. [? Tony, ?] yes? 01:24:24.420 --> 01:24:30.030 AUDIENCE: If you complete two matches at once, would it see both? 01:24:30.030 --> 01:24:31.680 SPEAKER 1: It would. 01:24:31.680 --> 01:24:34.680 The question was if you complete two matches at once, would it see both? 01:24:34.680 --> 01:24:35.280 Yes. 01:24:35.280 --> 01:24:39.570 If you complete-- and it wouldn't if you deleted 01:24:39.570 --> 01:24:43.790 them as you went because let's say you had like one, two, three here. 01:24:43.790 --> 01:24:46.890 I'm assuming that's what you mean, one, two, three, one, two, three. 01:24:46.890 --> 01:24:52.080 If you just deleted them as you went, then no, it wouldn't see them. 01:24:52.080 --> 01:24:55.450 It would go here, it would get these three, delete them, 01:24:55.450 --> 01:24:57.180 and then it would just see these two. 01:24:57.180 --> 01:24:59.730 But because we walk over the entire thing, 01:24:59.730 --> 01:25:03.030 and then we only delete matches after all of the matches have processed, 01:25:03.030 --> 01:25:06.314 we're going to add this one first, and then when we do our vertical one, 01:25:06.314 --> 01:25:09.480 we're also going to see this one, and so it's going to count as two matches. 01:25:09.480 --> 01:25:12.330 And you could make your code a little bit more complicated if you wanted to, 01:25:12.330 --> 01:25:14.820 and say if there's an intersection between two matches, 01:25:14.820 --> 01:25:16.900 I want to give the player more points. 01:25:16.900 --> 01:25:20.364 Or I want to give him some sort of effect like in Candy Crush, 01:25:20.364 --> 01:25:22.280 I think you get like explosions, or Bejeweled, 01:25:22.280 --> 01:25:25.450 you get explosions if you get like a T pattern. 01:25:25.450 --> 01:25:29.530 And if you get four in a row, you get a laser or something across the screen. 01:25:29.530 --> 01:25:32.380 And actually, part of assignment is clear a row. 01:25:32.380 --> 01:25:37.990 If you get four in a row, you should clear that row, or call them. 01:25:37.990 --> 01:25:40.360 If you do that, then yeah, you can have logic. 01:25:40.360 --> 01:25:44.290 But currently, all the distro does is just this simple iteration-- 01:25:44.290 --> 01:25:46.990 horizontally, then vertically, and adding matches as you go. 01:25:46.990 --> 01:25:49.690 And actually, there is an optimization that you can make. 01:25:49.690 --> 01:25:54.700 If you go here, for example, let's say we're going here, here, 01:25:54.700 --> 01:25:59.860 and then we're here, and we're at a different color than the last one. 01:25:59.860 --> 01:26:01.870 We can just go to the next one. 01:26:01.870 --> 01:26:05.440 We can just skip because we know we only have two left. 01:26:05.440 --> 01:26:10.090 There's no point in looking for a match if you're at the n minus 2 01:26:10.090 --> 01:26:12.170 because there's no possible way to get a match. 01:26:12.170 --> 01:26:13.060 So that's just a shortcut. 01:26:13.060 --> 01:26:15.893 A little optimization you can make, and that's actually in the code. 01:26:15.893 --> 01:26:17.690 Just break off if you're at-- 01:26:17.690 --> 01:26:21.910 in the code, it's x or y equals 7. 01:26:21.910 --> 01:26:23.830 Just break out of that for loop basically, 01:26:23.830 --> 01:26:28.780 and go to the next row, or column. 01:26:32.500 --> 01:26:34.810 Any more questions as to how that works? 01:26:38.170 --> 01:26:41.770 If you're actually looking in the code, we won't go over it 01:26:41.770 --> 01:26:44.152 in too much detail in class. 01:26:44.152 --> 01:26:46.360 It's fairly straightforward, I think, once I walk you 01:26:46.360 --> 01:26:52.060 through the algorithm a little bit, but I'll point you to the relevant lines. 01:26:52.060 --> 01:26:53.150 It's in the play state. 01:26:53.150 --> 01:26:53.650 No, sorry. 01:26:53.650 --> 01:26:59.020 It's in the board in the calculate matches function. 01:26:59.020 --> 01:27:04.840 Here, on line 50, calculate matches. 01:27:04.840 --> 01:27:08.180 So horizontal matches, y gets 1 to 8. 01:27:08.180 --> 01:27:10.337 You keep a color to match, and basically, you just 01:27:10.337 --> 01:27:11.920 keep track of how many you've matched. 01:27:11.920 --> 01:27:16.090 Match numbers one always when you're doing a brand new color, 01:27:16.090 --> 01:27:18.310 and then starting at x 2 to 8, because we already 01:27:18.310 --> 01:27:24.490 got the first tile, basically, if the color is the same, increment matchNum. 01:27:24.490 --> 01:27:29.800 Otherwise, set our current color to that color, the next tile. 01:27:29.800 --> 01:27:36.010 If we've done this, and our match is greater than or equal to 3, 01:27:36.010 --> 01:27:37.019 then we found a match. 01:27:37.019 --> 01:27:37.810 We can add a match. 01:27:37.810 --> 01:27:39.090 We create a new table. 01:27:39.090 --> 01:27:43.030 We go backwards from where we are with x 2 gets x minus 1, 01:27:43.030 --> 01:27:44.380 and then x minus matchNum. 01:27:44.380 --> 01:27:46.780 So it works for no matter how long the match is, 01:27:46.780 --> 01:27:49.420 whether it's three, four, or five. 01:27:49.420 --> 01:27:51.490 And then we're subtracting 1, and then you just 01:27:51.490 --> 01:27:56.560 insert into that match, the tile at that x 2 position 01:27:56.560 --> 01:27:58.570 because the matches are made of tiles. 01:27:58.570 --> 01:28:01.400 So a match is just a group of tiles put together, 01:28:01.400 --> 01:28:04.660 and so you can intersect to any given match just by comparing the tiles, 01:28:04.660 --> 01:28:06.951 and just seeing if they have the same tiles, basically. 01:28:06.951 --> 01:28:09.940 That's how you'd get a cross match. 01:28:09.940 --> 01:28:14.440 And then after that's all done, just insert into matches that match. 01:28:14.440 --> 01:28:15.940 Here's a little optimization. 01:28:15.940 --> 01:28:19.120 If x is greater than or equal to 7, and this is in part of the loop 01:28:19.120 --> 01:28:23.210 where we already know that we're on a new color from the last color, 01:28:23.210 --> 01:28:27.880 we'll just break, and then set matchNum to 1 if we haven't gotten to that point 01:28:27.880 --> 01:28:28.600 yet. 01:28:28.600 --> 01:28:33.700 And then this is the part of the code that accounts for a last row-- 01:28:33.700 --> 01:28:37.330 the row ending with a match because we're not going to be on the next loop 01:28:37.330 --> 01:28:39.880 to see whether we're going to a different color. 01:28:39.880 --> 01:28:45.414 We just need to check to make sure at the end of any row iteration, 01:28:45.414 --> 01:28:47.830 or column iteration when we go to the next row, or column. 01:28:47.830 --> 01:28:51.310 Before we go to the next row or column, that matchNum 01:28:51.310 --> 01:28:53.860 is greater than or equal to 3, and if so, 01:28:53.860 --> 01:28:57.430 then do the same logic here, but start x at 8. 01:28:57.430 --> 01:28:58.950 And same thing for vertical matches. 01:28:58.950 --> 01:29:02.260 Exact same logic, just x and y are inverted. 01:29:02.260 --> 01:29:03.970 And then that's it. 01:29:03.970 --> 01:29:08.080 And then self.matches, we keep a reference to self.matches in the board 01:29:08.080 --> 01:29:12.670 so that later we can remove them here, and I 01:29:12.670 --> 01:29:15.750 believe we use it for something else. 01:29:15.750 --> 01:29:21.070 And then we return, basically, if the number of matches is greater than 0, 01:29:21.070 --> 01:29:24.937 we're going to return matches, else we're just going to return false. 01:29:24.937 --> 01:29:26.020 And we can use this later. 01:29:26.020 --> 01:29:29.470 We can say if matches from our play state, 01:29:29.470 --> 01:29:32.320 then we can call a few other functions, and bring 01:29:32.320 --> 01:29:34.330 in new tiles, and stuff like that. 01:29:34.330 --> 01:29:39.950 But just for the sake of being thorough as an illustration, 01:29:39.950 --> 01:29:41.650 this is how the algorithm works. 01:29:41.650 --> 01:29:44.750 In this case, actually, this was before I made the optimization. 01:29:44.750 --> 01:29:47.575 We wouldn't actually do this in this particular case. 01:29:47.575 --> 01:29:52.330 This would have shorted down to the next column before it even checked this, 01:29:52.330 --> 01:29:55.560 but if your algorithm didn't make that optimization, then yeah, 01:29:55.560 --> 01:29:56.950 I would just see two tiles there. 01:29:56.950 --> 01:30:00.070 Go to the next one, nothing there, no matches. 01:30:00.070 --> 01:30:02.200 Same thing here. 01:30:02.200 --> 01:30:04.600 There is a match there, and the match would 01:30:04.600 --> 01:30:09.470 be found not at the end of the diagram here, 01:30:09.470 --> 01:30:11.950 it would be calculated when it's pointed here, 01:30:11.950 --> 01:30:17.400 but it knows matchNum is greater than or equal to 3 at that point. 01:30:17.400 --> 01:30:19.780 And it does the exact same thing here. 01:30:19.780 --> 01:30:22.540 We just go column wise, and then nothing there, 01:30:22.540 --> 01:30:25.790 nothing there, and then we've got one right there. 01:30:25.790 --> 01:30:27.530 And so the next part-- 01:30:27.530 --> 01:30:29.845 oh, any questions on how that works at all? 01:30:34.310 --> 01:30:36.652 The next part-- we have the matches now. 01:30:36.652 --> 01:30:37.610 We have them in tables. 01:30:37.610 --> 01:30:38.360 We have the tiles. 01:30:38.360 --> 01:30:39.890 We have references to the tiles. 01:30:39.890 --> 01:30:42.470 How do we remove the tiles once we have-- 01:30:42.470 --> 01:30:45.686 how do we get rid of them as soon as we have the matches? 01:30:52.200 --> 01:30:59.640 Assuming that our board is a table, a 2D table, and each array within there just 01:30:59.640 --> 01:31:04.691 has a tile object, how would we clear the board of our tiles? 01:31:04.691 --> 01:31:08.130 AUDIENCE: Are you including what you're shifting [INAUDIBLE]?? 01:31:08.130 --> 01:31:11.640 SPEAKER 1: No, just remove it from-- just like like. 01:31:11.640 --> 01:31:12.770 Just remove it from play. 01:31:12.770 --> 01:31:15.590 AUDIENCE: Oh. 01:31:15.590 --> 01:31:21.326 I guess you can [INAUDIBLE] 01:31:21.326 --> 01:31:22.450 SPEAKER 1: Yeah, you could. 01:31:22.450 --> 01:31:24.699 Yeah, with a little bit of finagling, you could get it 01:31:24.699 --> 01:31:27.790 to where you could set a tile to be invisible, 01:31:27.790 --> 01:31:31.790 and then you could just give it a new tile ID, I guess, 01:31:31.790 --> 01:31:36.550 and then shift it up above, and then make it come. 01:31:36.550 --> 01:31:40.600 Well, I don't know if that approach necessarily 01:31:40.600 --> 01:31:42.730 works super well for this because of gravity 01:31:42.730 --> 01:31:44.290 because the tiles have to come down. 01:31:44.290 --> 01:31:48.970 So then you'd have to bring the lower ones, if they were at the bottom here. 01:31:48.970 --> 01:31:50.560 Those would have to come down. 01:31:52.672 --> 01:31:54.630 That kind of approach would be a little tricky. 01:31:54.630 --> 01:31:58.090 You could make it work, I think. 01:31:58.090 --> 01:32:01.450 The simple approach, which we used in this distro, 01:32:01.450 --> 01:32:05.050 is actually just setting them to nil because if you set something to nil, 01:32:05.050 --> 01:32:07.206 it's just not going to render, in this case. 01:32:07.206 --> 01:32:10.330 So we're just setting all of these tiles that were previously there to nil. 01:32:10.330 --> 01:32:12.310 They're nothing at this point. 01:32:12.310 --> 01:32:16.300 They effectively would render like this if you tried to render them, 01:32:16.300 --> 01:32:20.930 assuming that your code accounted for it, or it didn't break. 01:32:20.930 --> 01:32:27.670 And then the next stage would be the actual getting the board fixed 01:32:27.670 --> 01:32:30.310 because we have the tiles removed. 01:32:30.310 --> 01:32:34.250 So now, we have this thing here, but there 01:32:34.250 --> 01:32:37.810 is a step that has to happen before we get new tiles, and that's gravity. 01:32:37.810 --> 01:32:40.000 We have to actually shift everything down. 01:32:40.000 --> 01:32:44.072 So how do we go about shifting tiles down? 01:32:44.072 --> 01:32:46.780 So this first column, we don't really have to do anything, right? 01:32:46.780 --> 01:32:49.049 This column is all set, but what about this column? 01:32:49.049 --> 01:32:49.840 How would we shift? 01:32:49.840 --> 01:32:52.968 How would we get that tile to go down? 01:32:52.968 --> 01:32:54.292 AUDIENCE: [INAUDIBLE] 01:32:54.292 --> 01:32:55.000 SPEAKER 1: Sorry? 01:32:55.000 --> 01:32:56.170 AUDIENCE: Tweens again? 01:32:56.170 --> 01:32:57.220 SPEAKER 1: Tweens. 01:32:57.220 --> 01:33:00.790 Yes, we could do it with tweens, but from a data structure 01:33:00.790 --> 01:33:02.050 standpoint, how would we-- 01:33:02.050 --> 01:33:05.262 because that will just tween the xy, but that won't necessarily 01:33:05.262 --> 01:33:06.970 fix-- the underlying data structure still 01:33:06.970 --> 01:33:09.820 has to represent-- because we're going to do iterations over it, 01:33:09.820 --> 01:33:12.520 we have to have references to the tiles in the right spots. 01:33:12.520 --> 01:33:15.130 AUDIENCE: So just shifting it was the table 01:33:15.130 --> 01:33:17.260 from the fourth row to the fifth row. 01:33:17.260 --> 01:33:20.890 SPEAKER 1: So how would you start by getting this tile down 01:33:20.890 --> 01:33:23.704 to this position? 01:33:23.704 --> 01:33:26.470 AUDIENCE: Switching it from the fourth row to the fifth row. 01:33:26.470 --> 01:33:27.655 SPEAKER 1: You would. 01:33:27.655 --> 01:33:31.939 How would your algorithm work step by step to making sure that would happen? 01:33:36.669 --> 01:33:41.019 AUDIENCE: Is it start from the bottom, and if that's nil, it would go up more 01:33:41.019 --> 01:33:41.810 SPEAKER 1: Exactly. 01:33:41.810 --> 01:33:43.660 That's exactly you do. 01:33:43.660 --> 01:33:49.790 You start from the bottom, and then whenever we have anything that's nil, 01:33:49.790 --> 01:33:53.910 we need to look for the first tile above it that's not nil, and shift it down. 01:33:53.910 --> 01:33:57.350 So in this case, we start from the bottom, and we go up this way. 01:33:57.350 --> 01:34:00.380 Not nil, not nil, not nil, not nil, not nil. 01:34:00.380 --> 01:34:02.630 So none of those are spaces in the code. 01:34:02.630 --> 01:34:07.626 It's called spaceY, or space and spaceY. 01:34:07.626 --> 01:34:09.500 So we go to the next column over, and we only 01:34:09.500 --> 01:34:11.610 have to check vertically in this case. 01:34:11.610 --> 01:34:13.735 We don't have to do a horizontal check for anything 01:34:13.735 --> 01:34:15.870 because gravity can only follow in one direction. 01:34:15.870 --> 01:34:17.120 So we just go over here. 01:34:17.120 --> 01:34:21.770 So we're only looping through this code, effectively, in this case, five times, 01:34:21.770 --> 01:34:24.770 but in our code, eight times. 01:34:24.770 --> 01:34:28.400 But it needs to be a while loop rather than a for loop, 01:34:28.400 --> 01:34:29.820 and we'll see why in a second. 01:34:29.820 --> 01:34:31.460 But start here. 01:34:31.460 --> 01:34:33.650 We see oh, we have a space there. 01:34:33.650 --> 01:34:39.080 So what we need to do is say, OK, the lowest space is here. 01:34:39.080 --> 01:34:42.764 So we need to look for the next tile above it, and shift it down. 01:34:42.764 --> 01:34:44.930 So we keep a reference to this, and we look up here, 01:34:44.930 --> 01:34:46.520 and we say oh, this is a tile. 01:34:46.520 --> 01:34:47.300 Perfect. 01:34:47.300 --> 01:34:49.510 So I'm just going to take this tile, and I'm 01:34:49.510 --> 01:34:52.460 going to set that space index to that tile, 01:34:52.460 --> 01:34:55.010 and then I'm going to set this index to nil. 01:34:55.010 --> 01:34:58.460 And then we just have to start again, though, from here 01:34:58.460 --> 01:35:01.670 because this tile is now space. 01:35:01.670 --> 01:35:06.770 So we have to look up here, and say OK, so basically, our y counter 01:35:06.770 --> 01:35:10.670 stays at that thing, and then just goes back up because our y counter could, 01:35:10.670 --> 01:35:13.310 theoretically, come all the way up here before it finds a tile, 01:35:13.310 --> 01:35:17.450 and then shift it all the way down, but we can't just-- or here, let's 01:35:17.450 --> 01:35:19.220 say there are two tiles right here. 01:35:19.220 --> 01:35:22.400 Our y counter might end up here because these are all spaces, 01:35:22.400 --> 01:35:25.220 and the tile gets shifted down here, but we can't just 01:35:25.220 --> 01:35:28.670 start our y counter back here again, and go up to the next tile, 01:35:28.670 --> 01:35:31.580 and look for spaces because we have all these spaces down here. 01:35:31.580 --> 01:35:32.520 So it's a while loop. 01:35:32.520 --> 01:35:36.690 So while, basically, there are no spaces on any of these points, 01:35:36.690 --> 01:35:39.080 we need to make sure that we keep lowering the tile. 01:35:39.080 --> 01:35:43.010 So keep a reference here, tile here, bring it down, space here. 01:35:43.010 --> 01:35:44.690 So we keep a reference with space. 01:35:44.690 --> 01:35:46.460 We say oh, there's a space here now. 01:35:46.460 --> 01:35:48.935 We can look all the way up, but there's no tiles anywhere. 01:35:48.935 --> 01:35:51.810 So we know that we can just move onto the next iteration of the loop. 01:35:51.810 --> 01:35:52.935 We haven't found any tiles. 01:35:52.935 --> 01:35:54.300 We don't need to bother with it. 01:35:54.300 --> 01:35:55.370 Same thing here. 01:35:55.370 --> 01:35:59.450 We have a space reference here, tile, found a tile, 01:35:59.450 --> 01:36:04.970 shift it down, space here, tile here, shift it down, space here, tile here, 01:36:04.970 --> 01:36:09.240 shift it down, space, space, done, and then we rinse, and repeat that. 01:36:09.240 --> 01:36:13.010 It's kind of almost like a bubble sort type of algorithm. 01:36:13.010 --> 01:36:18.180 Not a sort, but it has the same sort of look and behavior to it, more or less. 01:36:18.180 --> 01:36:20.780 Here's just a visual illustration of it. 01:36:20.780 --> 01:36:24.050 So start from the bottom, go up, we're looking for spaces here. 01:36:24.050 --> 01:36:26.510 No spaces; column is perfectly stable. 01:36:26.510 --> 01:36:30.440 We found a space here, tile is there, shift it down. 01:36:30.440 --> 01:36:35.650 Restart the loop from the tile, space, space, space, no more spaces; 01:36:35.650 --> 01:36:36.710 column stable. 01:36:36.710 --> 01:36:42.350 Space found, tile found, shift, space found, tile found, shift, space found, 01:36:42.350 --> 01:36:46.100 tile found, shift, and so on, and so forth. 01:36:46.100 --> 01:36:47.960 And so that's the gist. 01:36:47.960 --> 01:36:50.160 Super, super basic. 01:36:50.160 --> 01:36:54.705 But now we actually have to replace the tiles. 01:36:54.705 --> 01:36:56.330 AUDIENCE: You don't even need to check. 01:36:56.330 --> 01:36:58.330 Once you shift a block down, you don't even 01:36:58.330 --> 01:37:00.162 need to check the space above it, whether it's a space 01:37:00.162 --> 01:37:02.911 or not because you know that's automatically a space when you just 01:37:02.911 --> 01:37:05.294 shifted a block, right? 01:37:05.294 --> 01:37:05.960 SPEAKER 1: Yeah. 01:37:05.960 --> 01:37:06.980 Actually, that's true. 01:37:06.980 --> 01:37:10.460 Yeah, I guess in that case, you wouldn't need to. 01:37:10.460 --> 01:37:16.530 But we do need a reference to that space, and keep checking above it. 01:37:16.530 --> 01:37:19.430 But yeah, I guess you probably don't need, necessarily, 01:37:19.430 --> 01:37:21.200 to check whether it's a space or not. 01:37:21.200 --> 01:37:25.460 You can just assume it's a space, and I actually think my code does that. 01:37:25.460 --> 01:37:28.790 I'm not 100% sure off the top of my head. 01:37:28.790 --> 01:37:29.840 We can check, and see. 01:37:35.882 --> 01:37:37.840 I think it's down here. 01:37:37.840 --> 01:37:38.920 No? 01:37:38.920 --> 01:37:39.420 Is it? 01:37:42.260 --> 01:37:48.920 Oh no, it's in get falling tiles, I think, which is on line 177. 01:37:48.920 --> 01:37:54.890 So for 1 to 8 in x, we keep a spaceY. 01:37:54.890 --> 01:37:57.617 So spaceY, we set it zero because that's just a variable. 01:37:57.617 --> 01:37:58.700 We don't have a space yet. 01:37:58.700 --> 01:38:01.100 So just because you can't index a tile-- 01:38:01.100 --> 01:38:04.910 you can index Lua tables by zero, but because they're not by default, 01:38:04.910 --> 01:38:09.740 we're just setting the zero as like our false space flag. 01:38:09.740 --> 01:38:12.170 y gets 8, starting at the bottom. 01:38:12.170 --> 01:38:17.090 So while y is greater than or equal to 1, tile gets self.tiles y of x. 01:38:17.090 --> 01:38:21.230 In that case, it's going to be at the eighth position. 01:38:21.230 --> 01:38:23.930 So space is set to false, but space is our space 01:38:23.930 --> 01:38:27.830 found flag, and also whether or not the tile that we just looked at 01:38:27.830 --> 01:38:29.360 was a space. 01:38:29.360 --> 01:38:32.552 Sorry, no, it's just our space flag. 01:38:32.552 --> 01:38:35.010 We check to see if there is a tile at our current position. 01:38:35.010 --> 01:38:37.370 So recall, everything gets set to nil. 01:38:37.370 --> 01:38:41.050 So we can just say local tile gets self.tiles y x. 01:38:41.050 --> 01:38:42.910 This will be nil if there was no tile there. 01:38:42.910 --> 01:38:49.450 So if tile, which means if it's not nil, if it equal something, spaceY of x 01:38:49.450 --> 01:38:52.240 is going to equal that tile. 01:38:52.240 --> 01:38:56.230 We keep a reference to spaceY, which is our last space. 01:38:56.230 --> 01:39:03.340 We set tile.gridY to spaceY because we have to reset it to gridY. 01:39:03.340 --> 01:39:04.620 We're going to tween it here. 01:39:04.620 --> 01:39:07.720 This is how we actually get the falling, tweening behavior. 01:39:07.720 --> 01:39:12.070 We're going to tween it's y to tile.gridY minus 1 times 32, 01:39:12.070 --> 01:39:19.970 recall, because coordinates are zero based, but Lua tables are one indexed. 01:39:19.970 --> 01:39:25.745 Space is false, y is spaceY, and then spaceY gets zero. 01:39:28.390 --> 01:39:32.050 Basically, we're going to start at the-- 01:39:32.050 --> 01:39:37.360 we're going to put spaceY to that tile, and then we're 01:39:37.360 --> 01:39:41.980 going to set spaceY to 0. 01:39:41.980 --> 01:39:46.780 I think it actually does, in this case, it is actually checking that tile 01:39:46.780 --> 01:39:49.160 to make sure that it's-- 01:39:49.160 --> 01:39:52.086 yeah, because it's just getting set to the tile-- 01:39:52.086 --> 01:39:54.460 spaceY being the tile that we just replaced, and just put 01:39:54.460 --> 01:39:55.660 into an actual spot. 01:39:55.660 --> 01:39:58.701 So it does actually make the check up above to see whether that's a space 01:39:58.701 --> 01:40:00.220 or not. 01:40:00.220 --> 01:40:04.050 Only one caveat though actually is-- 01:40:04.050 --> 01:40:05.550 actually, no, that wouldn't be true. 01:40:05.550 --> 01:40:07.841 I was going to say, if you're at the top of the screen, 01:40:07.841 --> 01:40:12.850 but no because there's no way we can be at the top of the screen, 01:40:12.850 --> 01:40:17.590 and have-- yeah, I don't think it would work. 01:40:17.590 --> 01:40:20.710 A small optimization you could make is you just assume always a space. 01:40:20.710 --> 01:40:21.209 Yeah. 01:40:24.580 --> 01:40:26.980 That's the get falling tiles in a nut shell, 01:40:26.980 --> 01:40:30.460 or at least the ones that are falling from gravity. 01:40:30.460 --> 01:40:35.020 And then we also have tiles that we want to add to replace them, 01:40:35.020 --> 01:40:38.240 and so we'll see that here. 01:40:38.240 --> 01:40:39.790 So this code. 01:40:39.790 --> 01:40:44.310 So what we need to do to replace-- 01:40:44.310 --> 01:40:47.868 what do you guys think we need to do to get replacement tiles? 01:40:52.708 --> 01:40:56.580 AUDIENCE: Check response, and check if it's empty, [INAUDIBLE] 01:40:56.580 --> 01:40:58.709 but if it's not, then you're done. 01:40:58.709 --> 01:40:59.750 SPEAKER 1: Yeah, exactly. 01:40:59.750 --> 01:41:03.050 So the response was check to see from the top 01:41:03.050 --> 01:41:06.830 if there are any tiles that are empty, and if there are, 01:41:06.830 --> 01:41:11.360 then spawn some tiles, and then ideally, tween them to their new positions. 01:41:11.360 --> 01:41:15.419 You can basically just assign them to their values here. 01:41:15.419 --> 01:41:17.210 So what we need to do, actually, though, is 01:41:17.210 --> 01:41:21.920 if we spawn a tile up here to put into any of these positions, their gridY's 01:41:21.920 --> 01:41:23.690 need to be set in advance because they're 01:41:23.690 --> 01:41:25.740 going to occupy that space anyway. 01:41:25.740 --> 01:41:31.490 Their actual y position needs to be tweened. 01:41:31.490 --> 01:41:35.150 So because the x and the y are separate from the gridY, and the gridX, 01:41:35.150 --> 01:41:38.692 those are just table indices, but not their coordinates. 01:41:38.692 --> 01:41:40.400 We can tween those, and it won't actually 01:41:40.400 --> 01:41:41.983 have any effect on the data structure. 01:41:41.983 --> 01:41:46.070 The data structure itself can maintain-- we can still use the data structure-- 01:41:46.070 --> 01:41:48.350 put a tile in its right spot in our table, 01:41:48.350 --> 01:41:53.667 and then give it the right gridX, and gridY, but tween the x and y value. 01:41:53.667 --> 01:41:55.250 We can do whatever we want with those. 01:41:55.250 --> 01:41:58.220 We can make them spin around, and stuff as long 01:41:58.220 --> 01:42:01.430 as the data structure is intact, and ideally, 01:42:01.430 --> 01:42:04.100 as long as we can't input while it's doing it's movement, 01:42:04.100 --> 01:42:06.770 and stuff like that because that could create some visual bugs. 01:42:06.770 --> 01:42:10.670 And so what we do is we actually disable input when a swap is taking place, 01:42:10.670 --> 01:42:14.240 and you'll see that in the distribution code. 01:42:14.240 --> 01:42:16.880 But yes, count how many spaces there are. 01:42:16.880 --> 01:42:19.670 Spawn four tiles, spawn two tiles, spawn two tiles 01:42:19.670 --> 01:42:24.110 spawn four tiles that have already been given their right gridX, gridY, 01:42:24.110 --> 01:42:27.170 and then just tween their y to wherever it needs to go. 01:42:27.170 --> 01:42:30.580 It's gridY times 32-- 01:42:30.580 --> 01:42:33.110 gridY minus 1 times 32. 01:42:33.110 --> 01:42:34.610 And so that's what we're doing here. 01:42:34.610 --> 01:42:39.980 We're just count, and then boop. 01:42:39.980 --> 01:42:43.624 That was my favorite part of putting this show together. 01:42:43.624 --> 01:42:46.790 And so we're going to get into a couple of minutes of talking about sprites, 01:42:46.790 --> 01:42:50.164 and palettes, but I think the one thing-- 01:42:50.164 --> 01:42:51.080 blanking for a second. 01:42:51.080 --> 01:42:53.600 I was going to talk about one last thing. 01:42:53.600 --> 01:42:57.590 Let me see if I can figure out what that was. 01:42:57.590 --> 01:43:02.960 Oh, right, so in the board-- 01:43:02.960 --> 01:43:08.360 sorry, in the play state, I believe, is where this is, there is a function. 01:43:12.460 --> 01:43:15.980 So play state has it's own calculateMatches, basically, 01:43:15.980 --> 01:43:17.360 where it waits for you to-- 01:43:17.360 --> 01:43:20.690 where once you've basically swapped any two tiles, 01:43:20.690 --> 01:43:23.390 it will calculate whether those tiles have made a match. 01:43:26.150 --> 01:43:29.480 And we're going to get matches via self.board calculateMatches, 01:43:29.480 --> 01:43:31.520 the function that we were looking at before. 01:43:31.520 --> 01:43:32.810 If there are any matches-- 01:43:32.810 --> 01:43:35.609 well, we play a sound effect here for every match. 01:43:35.609 --> 01:43:37.400 This is where you also calculate the score. 01:43:37.400 --> 01:43:41.312 You just multiply the number of tiles in a match by 50, 01:43:41.312 --> 01:43:43.880 and part of the assignment will be adding some value 01:43:43.880 --> 01:43:45.890 to the individual varieties of the tiles. 01:43:48.890 --> 01:43:52.190 Here, we tween. 01:43:52.190 --> 01:43:58.220 So we return also from the board class a table 01:43:58.220 --> 01:44:02.810 of tweens for all of the new tiles that we just spawned, 01:44:02.810 --> 01:44:07.740 and so what we're going to end up doing is tweening all of them here. 01:44:07.740 --> 01:44:10.100 So notice that we're passing in a timer.tween, 01:44:10.100 --> 01:44:11.870 this variable, tilesToFall. 01:44:11.870 --> 01:44:15.650 That's a definition file that we're just returning from our board class. 01:44:15.650 --> 01:44:21.950 And so once those are all finished, then we get new tiles, 01:44:21.950 --> 01:44:25.620 and then we tween here. 01:44:25.620 --> 01:44:28.074 I think this line is redundant, actually. 01:44:28.074 --> 01:44:29.990 I think this might have been a debugging line. 01:44:29.990 --> 01:44:31.220 I don't think we need this. 01:44:33.750 --> 01:44:35.000 No, we don't need this at all. 01:44:35.000 --> 01:44:36.650 So sorry. 01:44:36.650 --> 01:44:39.260 This is the important part. 01:44:39.260 --> 01:44:41.720 We're going to tween-- 01:44:41.720 --> 01:44:42.560 wait, we do need it. 01:44:42.560 --> 01:44:45.480 Self.board getNewTiles. 01:44:45.480 --> 01:44:47.338 What am I thinking of? 01:44:53.570 --> 01:44:55.320 Sorry, a little bit confused for a second. 01:44:55.320 --> 01:44:58.012 I thought this was an empty function that I defined. 01:44:58.012 --> 01:44:59.970 Get new tiles. 01:44:59.970 --> 01:45:02.770 Yeah, this returns an empty table. 01:45:02.770 --> 01:45:06.940 But basically, the gist of it is the play 01:45:06.940 --> 01:45:13.485 state, when it calls this function, it will call itself every time. 01:45:15.884 --> 01:45:18.300 And I think this is actually having the result of doing it 01:45:18.300 --> 01:45:20.591 instantly here because newTiles is just an empty table. 01:45:20.591 --> 01:45:28.620 I think all this should be is just this inside all of this like that. 01:45:28.620 --> 01:45:31.980 But that has the result of calling itself again 01:45:31.980 --> 01:45:35.520 because when we get new tiles coming from the top of the screen, 01:45:35.520 --> 01:45:40.170 we could potentially have a case where we've gotten some matches, 01:45:40.170 --> 01:45:45.090 and it's not shown here, but new falling tiles could give us new matches. 01:45:45.090 --> 01:45:48.840 So after we calculate matches, let's say maybe this tile dropped here, 01:45:48.840 --> 01:45:51.135 but it was a purple, and these two were already there. 01:45:51.135 --> 01:45:53.010 We've already calculated matches, but then we 01:45:53.010 --> 01:45:56.690 need to do it again, and then do it again if it keeps happening. 01:45:56.690 --> 01:45:59.790 And so you should be recursively call self calculateMatches 01:45:59.790 --> 01:46:06.510 in that case, which will have the effect of accomplishing that because this 01:46:06.510 --> 01:46:07.980 will always look for matches. 01:46:07.980 --> 01:46:11.580 And so when we call self calculateMatches here, over, and over 01:46:11.580 --> 01:46:14.096 again, until there are no matches-- 01:46:14.096 --> 01:46:16.470 as long as there are matches, this should keep happening. 01:46:16.470 --> 01:46:19.980 You should keep getting scores, and tiles should keep getting cleared. 01:46:19.980 --> 01:46:22.470 But as soon as that's not the case anymore, 01:46:22.470 --> 01:46:27.005 then self.canInput equals true, and we're not calculating matches anymore. 01:46:27.005 --> 01:46:29.630 We don't recursively call the function anymore, and we're done. 01:46:29.630 --> 01:46:31.230 And so that's just the point I wanted to illustrate. 01:46:31.230 --> 01:46:34.500 Got slightly confused by, I think, what was a vestige of my old code. 01:46:34.500 --> 01:46:37.109 Maybe I was trying something, but I think this, ultimately, 01:46:37.109 --> 01:46:39.900 should just be this, and I'll test it, and make sure, and then push 01:46:39.900 --> 01:46:40.410 the change. 01:46:42.960 --> 01:46:44.972 And it doesn't need to be over 0.25 seconds. 01:46:44.972 --> 01:46:45.930 It can just be instant. 01:46:48.102 --> 01:46:50.560 Palettes, really quickly, with something I wanted to cover, 01:46:50.560 --> 01:46:53.606 which was just the idea of taking art, and then just-- and I 01:46:53.606 --> 01:46:55.230 have a couple of cool examples to show. 01:46:55.230 --> 01:46:58.080 Just taking some sort of picture, and then giving it-- 01:46:58.080 --> 01:47:03.060 only using or some sort of image, and only using 32, in this case, 01:47:03.060 --> 01:47:05.160 or some arbitrary number of colors. 01:47:05.160 --> 01:47:09.390 This is some fancy stuff that some person named DawnBringer online did. 01:47:09.390 --> 01:47:13.170 He generated a very famous 32 color palette called DawnBringer's 32 color 01:47:13.170 --> 01:47:14.760 palette. 01:47:14.760 --> 01:47:16.380 But basically, it allows-- 01:47:16.380 --> 01:47:18.990 this is done with just 32 colors we see on the screen. 01:47:18.990 --> 01:47:21.750 Those are all dithered. 01:47:21.750 --> 01:47:27.120 Dithering is a term which means to just draw two colors pixel by pixel, 01:47:27.120 --> 01:47:31.590 interleaved, so that from far away it looks like a brand new color, 01:47:31.590 --> 01:47:32.880 and this is a dithering chart. 01:47:32.880 --> 01:47:35.550 This just shows you every color here at the very top. 01:47:35.550 --> 01:47:37.570 These are all 32 colors. 01:47:37.570 --> 01:47:42.420 These are 32, and those are 32 intersected with each other such 01:47:42.420 --> 01:47:45.030 that they're just like dot, dot, dot, dot, dot. 01:47:45.030 --> 01:47:47.027 Every other dot is every other color. 01:47:47.027 --> 01:47:49.860 And so you can do some pretty amazing things with just a few colors. 01:47:49.860 --> 01:47:52.140 This is actually done with 16 colors. 01:47:52.140 --> 01:47:56.940 All four of those are only 16 colors. 01:47:56.940 --> 01:48:00.500 This is just to show you what it looks like when you do it to an actual image. 01:48:00.500 --> 01:48:04.159 This is an example of what using a color palette on an image that 01:48:04.159 --> 01:48:05.700 doesn't work well with it looks like. 01:48:05.700 --> 01:48:07.533 So this is a regular image with I don't know 01:48:07.533 --> 01:48:10.080 how many colors, thousands of millions of colors, and this 01:48:10.080 --> 01:48:12.760 is using DawnBringer's 32 color palette. 01:48:12.760 --> 01:48:15.390 So still looks very similar to what it should. 01:48:15.390 --> 01:48:19.800 It's a cat, but there's a lot of weird things going on in the background 01:48:19.800 --> 01:48:24.240 because taking an image with a lot of blur, and a lot of distorted color, 01:48:24.240 --> 01:48:28.230 has the effect of giving you blotchy patterns when you go down 01:48:28.230 --> 01:48:30.640 to a few colors. 01:48:30.640 --> 01:48:34.807 But this is an example of an image that has a lot of flatter colors. 01:48:34.807 --> 01:48:36.640 There's still a lot of colors in this image. 01:48:36.640 --> 01:48:40.800 There are some shades, and stuff like that, but this is thousands of colors, 01:48:40.800 --> 01:48:42.490 and this is 32 colors. 01:48:42.490 --> 01:48:45.509 So clearly, if you do it on the right thing, 01:48:45.509 --> 01:48:47.550 you can actually get really good effects with it. 01:48:47.550 --> 01:48:52.004 And so again, not a whole lot of difference, but this one's 01:48:52.004 --> 01:48:54.420 got I don't know how many hundreds of thousands of colors, 01:48:54.420 --> 01:48:57.450 and this one's only got 32. 01:48:57.450 --> 01:49:00.150 And so how it ties back into what we're doing 01:49:00.150 --> 01:49:06.910 is this is using a 32-bit color or 32 color palette on purpose. 01:49:06.910 --> 01:49:09.060 This is actually DawnBringer's 32 color palette. 01:49:09.060 --> 01:49:12.510 Breakout used the same palette, 32 colors, 01:49:12.510 --> 01:49:17.770 and a lot of our 2D future lectures will use limited color palettes. 01:49:17.770 --> 01:49:22.890 If you're trying to draw sprite art, and you want some quick, and easy ways just 01:49:22.890 --> 01:49:26.100 to give your work a little bit of consistency, 01:49:26.100 --> 01:49:30.180 I recommend trying to pick 8, or 16, or 32 colors, 01:49:30.180 --> 01:49:32.570 and just adhering to using just those. 01:49:32.570 --> 01:49:35.580 And you'd be surprised at how much you get out 01:49:35.580 --> 01:49:39.240 of it, and how much more cohesive your work will look just 01:49:39.240 --> 01:49:41.200 by imposing that constraint on you. 01:49:41.200 --> 01:49:46.170 It's an artifact of a real world constraint of former hardware. 01:49:46.170 --> 01:49:48.720 The NES only had so many colors it could color each sprite, 01:49:48.720 --> 01:49:51.570 like four colors, or something that. 01:49:51.570 --> 01:49:55.530 And so you also get a-- if you're going for an authentic retro look, 01:49:55.530 --> 01:49:56.920 it will help you in that sense. 01:49:56.920 --> 01:49:59.580 And then different from palettes, but related 01:49:59.580 --> 01:50:02.550 is palette swapping, which is another term you've probably heard, 01:50:02.550 --> 01:50:05.295 which is basically all of these Mario sprites-- 01:50:07.950 --> 01:50:12.600 they'll probably start with a gray scale Mario, some like gray version 01:50:12.600 --> 01:50:15.990 where each of these separate colors are mapped out 01:50:15.990 --> 01:50:20.430 to some table where one equals red, two equals blue, or whatever. 01:50:20.430 --> 01:50:23.340 And then you can just shift all of them, and then you 01:50:23.340 --> 01:50:25.470 get all of these different nice effects, assuming 01:50:25.470 --> 01:50:30.690 that you've created a good palette. 01:50:30.690 --> 01:50:33.480 You can get a lot of reuse, and this is actually 01:50:33.480 --> 01:50:38.310 how Super Mario Bros. used to do some of its programming. 01:50:38.310 --> 01:50:41.310 The clouds and the bushes were the same sprite. 01:50:41.310 --> 01:50:42.960 One was just colored green. 01:50:42.960 --> 01:50:48.780 It was palette swapped green from the white that the cloud was colored. 01:50:48.780 --> 01:50:51.830 So that's the gist of Match 3. 01:50:51.830 --> 01:50:54.150 Assignment 3 is going to have a few parts to it. 01:50:54.150 --> 01:50:55.740 So time addition on matches. 01:50:55.740 --> 01:50:58.800 So when you get a match, you should get time added to the clock. 01:50:58.800 --> 01:51:01.290 Currently, right now, you only get 60 seconds. 01:51:01.290 --> 01:51:05.130 It's a little bit hard to actually get past level two at this point. 01:51:05.130 --> 01:51:08.880 So getting points for every tile in a match. 01:51:08.880 --> 01:51:11.490 Make it so that level one starts with simple flat blocks. 01:51:11.490 --> 01:51:14.580 So earlier, we saw the array of tiles, and it 01:51:14.580 --> 01:51:18.625 was flat tiles on the first index of every color row, 01:51:18.625 --> 01:51:21.750 but there were several other patterns like x's, and circles, and triangles, 01:51:21.750 --> 01:51:22.410 and stuff. 01:51:22.410 --> 01:51:27.780 Make those worth some higher amount of value, each one. 01:51:27.780 --> 01:51:31.324 Create random shiny variants of blocks that will destroy an entire row when 01:51:31.324 --> 01:51:31.990 you get a match. 01:51:31.990 --> 01:51:32.930 So have a block. 01:51:32.930 --> 01:51:35.160 It should have some field, shiny or something. 01:51:35.160 --> 01:51:38.310 If it's shiny, render it with something to make it look shiny. 01:51:38.310 --> 01:51:41.370 You can use particle effect if you want. 01:51:41.370 --> 01:51:48.000 You can put a very opaque, or a very transparent maybe yellowish or whitish 01:51:48.000 --> 01:51:50.430 rectangle on it to give it a brighter look. 01:51:50.430 --> 01:51:53.820 And then if it's in a match, that entire row 01:51:53.820 --> 01:51:58.312 should get cleared instead of just that match. 01:51:58.312 --> 01:52:00.270 Only allow swapping when it results in a match. 01:52:00.270 --> 01:52:03.150 This is an important thing because right now, mathematically, 01:52:03.150 --> 01:52:05.850 it's actually very unlikely that you'll get a board that 01:52:05.850 --> 01:52:10.660 has matches on it to begin with. 01:52:10.660 --> 01:52:16.350 So you're going to have to pick a subset of tiles in your implementation, 01:52:16.350 --> 01:52:20.250 and actually use those instead of just using all of them. 01:52:20.250 --> 01:52:25.470 Pick six tiles, which you can get variants on, or just whatever flat 01:52:25.470 --> 01:52:28.920 colors, and then use only those to spawn your board. 01:52:28.920 --> 01:52:32.600 Don't use all 18, or however many there are. 01:52:32.600 --> 01:52:35.520 And then optional, if you're curious, if you want, probably, 01:52:35.520 --> 01:52:38.250 an arguably better gaming experience with this, 01:52:38.250 --> 01:52:41.490 just implement actually playing with the mouse. 01:52:41.490 --> 01:52:44.880 Being able to click and drag, or just click individual tiles. 01:52:44.880 --> 01:52:47.220 And to do that, you will need to convert-- 01:52:47.220 --> 01:52:49.620 because we use the push library for virtual resolution, 01:52:49.620 --> 01:52:53.340 you'll need to convert the window mouse coordinates 01:52:53.340 --> 01:52:56.584 to push coordinates so that they'll map into the game space appropriately, 01:52:56.584 --> 01:52:58.875 and so you'll use a function called push to game, where 01:52:58.875 --> 01:53:03.780 it takes an x and y, where the x and the y will be your mouse coordinates. 01:53:03.780 --> 01:53:06.450 Next time, we're actually going to get into a little bit 01:53:06.450 --> 01:53:11.880 more robust of a game, arguably, like a Mario clone. 01:53:11.880 --> 01:53:14.910 This is actually where this course started 01:53:14.910 --> 01:53:16.920 was I taught a seminar on Super Mario Bros. 01:53:16.920 --> 01:53:20.186 We won't be using Super Mario Bros. assets because of copyright, 01:53:20.186 --> 01:53:22.810 but we'll be using this tile sheet here, which is very similar. 01:53:22.810 --> 01:53:23.950 It's got a nice aesthetic. 01:53:23.950 --> 01:53:25.230 We'll cover tile maps. 01:53:25.230 --> 01:53:27.990 So how to generate levels using individual tiles. 01:53:27.990 --> 01:53:30.187 2D animation, so rather than just like static things 01:53:30.187 --> 01:53:33.270 that we've had going on so far, you'll have characters that actually walk, 01:53:33.270 --> 01:53:35.241 and jump, and do different things. 01:53:35.241 --> 01:53:36.990 We'll talk about how to actually procedure 01:53:36.990 --> 01:53:40.710 generate platformer levels, which isn't terribly difficult. 01:53:40.710 --> 01:53:43.630 It sounds kind of difficult, but it's actually pretty-- 01:53:43.630 --> 01:53:46.650 for very simple stuff, it's not too bad. 01:53:46.650 --> 01:53:49.097 Basic platformer physics, so hitting blocks, and jumping, 01:53:49.097 --> 01:53:49.930 and stuff like that. 01:53:49.930 --> 01:53:54.780 We've covered a lot of that with Flappy Bird, actually, and actually, 01:53:54.780 --> 01:53:57.890 the bricks from Breakout kind of tie into it a little bit. 01:53:57.890 --> 01:54:01.890 Hurt boxes so we can have enemies that hurt you, and visa versa. 01:54:01.890 --> 01:54:05.100 And power ups so that you can change your state in some way to make you 01:54:05.100 --> 01:54:07.620 larger, or invincible, or whatnot. 01:54:07.620 --> 01:54:08.520 That was Match 3. 01:54:08.520 --> 01:54:11.270 Thanks a lot, and see you next time.