1 00:00:00,000 --> 00:00:00,098 2 00:00:00,098 --> 00:00:00,890 SPEAKER: All right. 3 00:00:00,890 --> 00:00:05,040 In Mario5, we added the ability for our camera to track the player as opposed 4 00:00:05,040 --> 00:00:09,150 to just sort of scrolling independently of where the player was being rendered. 5 00:00:09,150 --> 00:00:11,980 In this example, we're going to dive into a couple of things. 6 00:00:11,980 --> 00:00:14,003 So this is called the animation update. 7 00:00:14,003 --> 00:00:15,920 And in order to get into animation, it's going 8 00:00:15,920 --> 00:00:17,500 to be a somewhat complicated process. 9 00:00:17,500 --> 00:00:18,960 So the first thing that you're going to notice 10 00:00:18,960 --> 00:00:20,490 is that we have our avatar there. 11 00:00:20,490 --> 00:00:23,640 We have a frame of animation as they're going towards the left. 12 00:00:23,640 --> 00:00:26,220 You can see they're very clearly looking towards the left. 13 00:00:26,220 --> 00:00:29,525 And it'll be the case that the avatar will also be able to look to the right. 14 00:00:29,525 --> 00:00:30,900 They'll have a walking animation. 15 00:00:30,900 --> 00:00:33,210 And then when we don't actually move, we want 16 00:00:33,210 --> 00:00:36,540 them to go back to their idle animation where they're sort of looking dead on. 17 00:00:36,540 --> 00:00:39,310 And this kind of helps us tie into a couple of concepts. 18 00:00:39,310 --> 00:00:42,420 So the first thing that we want to look at is sprite animation. 19 00:00:42,420 --> 00:00:45,660 And a sprite animation is really kind of like a flip-book 20 00:00:45,660 --> 00:00:48,750 like you might have seen where you have one picture on one page, 21 00:00:48,750 --> 00:00:50,130 then you have another picture on the next page, 22 00:00:50,130 --> 00:00:51,730 and another picture on the next page. 23 00:00:51,730 --> 00:00:53,730 And by flipping through them sort of quickly, 24 00:00:53,730 --> 00:00:56,790 it gives you the appearance of it being something that's actually moving. 25 00:00:56,790 --> 00:00:59,160 And this is, of course, the principle behind animation. 26 00:00:59,160 --> 00:01:02,310 2D and 3D animation, as we know it, is just a series of still frames 27 00:01:02,310 --> 00:01:05,110 that together form, or at least it tricks our minds into thinking, 28 00:01:05,110 --> 00:01:07,860 our eyes into thinking that we're watching something really moving 29 00:01:07,860 --> 00:01:09,360 and doing something on the screen. 30 00:01:09,360 --> 00:01:11,580 And we're going to be doing the same exact thing here 31 00:01:11,580 --> 00:01:13,860 with sprite animation where we take those quads 32 00:01:13,860 --> 00:01:16,560 that we've been chopping up before, and we put them together 33 00:01:16,560 --> 00:01:17,975 in a sort of a package. 34 00:01:17,975 --> 00:01:20,600 And then we loop through them, depending on some sort of timer. 35 00:01:20,600 --> 00:01:24,810 We're going to keep track of using DT how much time has elapsed. 36 00:01:24,810 --> 00:01:28,230 Over the course of several frames, we're going to cumulatively add that time. 37 00:01:28,230 --> 00:01:31,990 When it passes a certain amount of time, let's say 0.15 seconds, 38 00:01:31,990 --> 00:01:34,230 we'll say that it's time to go to the next frame. 39 00:01:34,230 --> 00:01:37,792 At which point, our animation class that we're going to use says, 40 00:01:37,792 --> 00:01:39,750 OK, this is the current frame of the animation. 41 00:01:39,750 --> 00:01:41,542 This is the current frame of the animation. 42 00:01:41,542 --> 00:01:43,890 And once the whole entire animation has elapsed, 43 00:01:43,890 --> 00:01:46,915 we've gone through all the frames, we'll loop back to the first frame 44 00:01:46,915 --> 00:01:50,040 over and over again, which will give us the appearance of sort of a looping 45 00:01:50,040 --> 00:01:51,098 animation. 46 00:01:51,098 --> 00:01:53,140 Another thing that we're going to take a look at, 47 00:01:53,140 --> 00:01:56,210 which is really important in this context, is state. 48 00:01:56,210 --> 00:01:58,710 Once again, we looked at state before in the context of Pong 49 00:01:58,710 --> 00:02:00,060 where we had different game states. 50 00:02:00,060 --> 00:02:02,220 But now, we have an entity that's going to behave somewhat 51 00:02:02,220 --> 00:02:05,160 similar to what we're seeing here where we actually do have a standing state. 52 00:02:05,160 --> 00:02:06,118 We have a moving state. 53 00:02:06,118 --> 00:02:07,410 We have a jumping state. 54 00:02:07,410 --> 00:02:09,360 Moving isn't actually diagrammed here. 55 00:02:09,360 --> 00:02:12,330 But if we are standing, and we move a direction, 56 00:02:12,330 --> 00:02:16,470 we should transition to the moving state, the walking state. 57 00:02:16,470 --> 00:02:18,480 If we're walking, and we stop moving, we should 58 00:02:18,480 --> 00:02:20,130 transition to the standing still state. 59 00:02:20,130 --> 00:02:21,930 And this should also reflect on the animation 60 00:02:21,930 --> 00:02:22,950 that's currently being presented. 61 00:02:22,950 --> 00:02:25,110 We should have two different animations-- one 62 00:02:25,110 --> 00:02:27,720 for moving and one for standing still. 63 00:02:27,720 --> 00:02:30,280 And if we're moving, we should play the moving animation. 64 00:02:30,280 --> 00:02:32,410 If we're standing still, we should play the standing still animation. 65 00:02:32,410 --> 00:02:33,630 So pretty straightforward. 66 00:02:33,630 --> 00:02:35,610 That's all the real framework that we'll lay 67 00:02:35,610 --> 00:02:37,110 before we actually get started here. 68 00:02:37,110 --> 00:02:38,943 The first thing that I want to do, probably, 69 00:02:38,943 --> 00:02:41,160 is start with the animation class. 70 00:02:41,160 --> 00:02:44,040 So let's go ahead and create Animation.lua. 71 00:02:44,040 --> 00:02:46,990 So animation is going to be equal to a class. 72 00:02:46,990 --> 00:02:50,940 I'm going to go ahead and preemptively load that here in Player. 73 00:02:50,940 --> 00:02:55,100 So I'm going to go ahead and require animation, just 74 00:02:55,100 --> 00:02:56,777 so I don't forget to do that later. 75 00:02:56,777 --> 00:02:59,610 And the animation is going to have an init, just like anything we've 76 00:02:59,610 --> 00:03:01,680 seen thus far. 77 00:03:01,680 --> 00:03:04,795 But the init is going to take a series of parameters. 78 00:03:04,795 --> 00:03:07,920 And the parameters are what's going to define not only the frames, but also 79 00:03:07,920 --> 00:03:10,170 the interval through which the animation is going 80 00:03:10,170 --> 00:03:12,330 to take place, the sort of amount of time 81 00:03:12,330 --> 00:03:15,900 in between each frame that's going to tell our timer how long we should wait 82 00:03:15,900 --> 00:03:18,250 in order to go to the next frame. 83 00:03:18,250 --> 00:03:21,360 So let's go ahead and say that this params is going to carry with it 84 00:03:21,360 --> 00:03:22,427 a texture. 85 00:03:22,427 --> 00:03:24,510 And this params is ultimately going to be a table. 86 00:03:24,510 --> 00:03:27,510 So we're going to index into it using dot syntax like this. 87 00:03:27,510 --> 00:03:29,760 And we're going to very easily be able to sort of pass 88 00:03:29,760 --> 00:03:31,872 this sort of a configuration object in that sense. 89 00:03:31,872 --> 00:03:33,330 So we're going to get self.texture. 90 00:03:33,330 --> 00:03:34,710 We're going to have some frames. 91 00:03:34,710 --> 00:03:37,140 And we're going to expect that we're going 92 00:03:37,140 --> 00:03:41,640 to have the frames sort of attribute on params, such that we 93 00:03:41,640 --> 00:03:44,520 can worry about passing in the quads from wherever 94 00:03:44,520 --> 00:03:46,650 we instantiate the animation. 95 00:03:46,650 --> 00:03:48,358 Next, we're going to declare an interval. 96 00:03:48,358 --> 00:03:50,317 And this is going to be the amount of time that 97 00:03:50,317 --> 00:03:51,930 should pass between each frame. 98 00:03:51,930 --> 00:03:54,300 And so we're going to say params.interval, 99 00:03:54,300 --> 00:03:57,360 or we're going to have a default of 0.05. 100 00:03:57,360 --> 00:04:01,230 So this or just means that if we don't get a params.interval within our table 101 00:04:01,230 --> 00:04:06,150 object that gets passed in, it's going to default to this 0.05 value. 102 00:04:06,150 --> 00:04:09,000 Now what we're going to do is declare also a self.timer. 103 00:04:09,000 --> 00:04:12,150 Now, this, it's going to be starting off as a zero value, 104 00:04:12,150 --> 00:04:15,750 but it's going to be incremented by delta time each and every frame. 105 00:04:15,750 --> 00:04:18,600 And we're going to have an update function down here that 106 00:04:18,600 --> 00:04:19,860 actually takes care of this. 107 00:04:19,860 --> 00:04:22,530 So I'm going to go ahead and say this update. 108 00:04:22,530 --> 00:04:24,613 And the very first thing we're going to do in here 109 00:04:24,613 --> 00:04:27,900 is say self.timer is equal to self.timer plus delta time. 110 00:04:27,900 --> 00:04:30,030 And what we're going to do pretty soon is, 111 00:04:30,030 --> 00:04:33,510 as we get delta time incremented onto our timer, all we have to do 112 00:04:33,510 --> 00:04:36,473 is check to see whether the timer is greater than the interval. 113 00:04:36,473 --> 00:04:38,640 And if that's the case, well, then the current frame 114 00:04:38,640 --> 00:04:40,740 is going to be needed to set to the next value. 115 00:04:40,740 --> 00:04:43,920 And accordingly, we need to have a self.current frame. 116 00:04:43,920 --> 00:04:45,990 By default, that's going to be one. 117 00:04:45,990 --> 00:04:48,780 And so this frames table here is essentially 118 00:04:48,780 --> 00:04:51,030 going to be reliant on this self.current frame. 119 00:04:51,030 --> 00:04:57,610 This will be the index into which we get the current frame from self.frames. 120 00:04:57,610 --> 00:05:02,958 So let's go ahead and write Animation:getCurrentFrame. 121 00:05:02,958 --> 00:05:06,000 This will be very useful when we're actually using this within the Player 122 00:05:06,000 --> 00:05:07,350 class to draw the Player. 123 00:05:07,350 --> 00:05:09,558 Depending on what stage the animation is in, 124 00:05:09,558 --> 00:05:11,850 we're going to use this getCurrentFrame function, which 125 00:05:11,850 --> 00:05:18,480 is going to return self.frames at self.currentFrame, just like so. 126 00:05:18,480 --> 00:05:21,532 And then one thing we should also have is a restart function just in case 127 00:05:21,532 --> 00:05:23,490 we want to transition between different states, 128 00:05:23,490 --> 00:05:26,100 but we don't want to start in the middle of, let's say, the walk state. 129 00:05:26,100 --> 00:05:27,933 Or if we have a state that's 20 frames long, 130 00:05:27,933 --> 00:05:29,763 or an animation that's 20 frames long, we 131 00:05:29,763 --> 00:05:32,430 don't want to start in the middle of it if we go back and forth. 132 00:05:32,430 --> 00:05:34,470 Because these animations are going to sort of 133 00:05:34,470 --> 00:05:37,207 maintain their state across transitions. 134 00:05:37,207 --> 00:05:40,290 In other words, if we transition from a walking state to a standing state, 135 00:05:40,290 --> 00:05:42,082 the walking state is going to kind of still 136 00:05:42,082 --> 00:05:44,640 have this timer information and whatnot stored within it. 137 00:05:44,640 --> 00:05:47,640 But we'd rather probably like to have it restart from scratch every time 138 00:05:47,640 --> 00:05:50,950 we transition between walking and standing and so forth. 139 00:05:50,950 --> 00:05:53,310 So in order to restart this, all I should do 140 00:05:53,310 --> 00:05:58,030 is say self.timer is equal to zero and self.currentFrame is equal to one. 141 00:05:58,030 --> 00:06:00,240 Now, let's go ahead and finish the timer class here. 142 00:06:00,240 --> 00:06:02,820 So there is a nice little shorthand for getting 143 00:06:02,820 --> 00:06:04,890 the length of a table inside of Lua. 144 00:06:04,890 --> 00:06:06,820 And that's this hash symbol. 145 00:06:06,820 --> 00:06:12,270 So if we say if hash of self.frames is equal to one, meaning if the length-- 146 00:06:12,270 --> 00:06:14,460 it's like the leng function in Python-- 147 00:06:14,460 --> 00:06:17,393 if the length of the self.frames table is equal to one, 148 00:06:17,393 --> 00:06:20,560 well, we don't really have much to do in terms of an animation in that case. 149 00:06:20,560 --> 00:06:24,900 So we'll just return self.currentFrame like so. 150 00:06:24,900 --> 00:06:29,700 Else, and this is where we're going to do the bulk of the actual math 151 00:06:29,700 --> 00:06:31,830 behind calculating the next stage of the animation. 152 00:06:31,830 --> 00:06:35,250 So what we can do is we can say while self.timer is greater 153 00:06:35,250 --> 00:06:37,910 than self.interval do. 154 00:06:37,910 --> 00:06:41,160 And what this just means is if we reach the point where our timer has actually 155 00:06:41,160 --> 00:06:44,040 gone past the interval, then we need to take 156 00:06:44,040 --> 00:06:47,040 care of incrementing the current frame, pointing to the next frame, 157 00:06:47,040 --> 00:06:48,960 and looping back to the start if we've gone 158 00:06:48,960 --> 00:06:51,240 past the sort of end of the animation. 159 00:06:51,240 --> 00:06:53,490 In other words, if we've gone to frame three, 160 00:06:53,490 --> 00:06:55,198 and it's time to go to the next frame, we 161 00:06:55,198 --> 00:06:58,080 should go to frame one, assuming that our animation is, let's say, 162 00:06:58,080 --> 00:06:59,520 three frames long. 163 00:06:59,520 --> 00:07:01,650 So I'm going to go ahead and say self.timer 164 00:07:01,650 --> 00:07:05,350 should be equal to self.timer minus self.interval. 165 00:07:05,350 --> 00:07:08,730 And notice that we're not just setting it to zero and going to the next frame. 166 00:07:08,730 --> 00:07:11,580 There could feasibly be a situation in which a lot of time 167 00:07:11,580 --> 00:07:13,890 passes between one frame and another frame. 168 00:07:13,890 --> 00:07:16,950 And this is greater than the interval, or maybe even two times 169 00:07:16,950 --> 00:07:18,700 the integral of the animation. 170 00:07:18,700 --> 00:07:22,170 So if it's the case that we've passed two or three or four 171 00:07:22,170 --> 00:07:25,950 x frames beyond just one, well, we want to take this into consideration 172 00:07:25,950 --> 00:07:29,040 by putting this in a while loop and decrementing timer by the interval, 173 00:07:29,040 --> 00:07:31,230 not by just setting it back to zero, which might 174 00:07:31,230 --> 00:07:33,900 be the first instinctive thing to do. 175 00:07:33,900 --> 00:07:38,500 So self.currentFrame should then be set to self.currentFrame. 176 00:07:38,500 --> 00:07:42,480 Though, this is where we end up looping the animation back to one. 177 00:07:42,480 --> 00:07:46,590 So we're going to say self.currentFrame plus one modulus 178 00:07:46,590 --> 00:07:53,430 from C, as you've probably seen before, the number of self.frames plus one. 179 00:07:53,430 --> 00:07:56,730 And we're doing this plus one such that when we get to frame three, 180 00:07:56,730 --> 00:08:00,480 we don't immediately loop back to frame one, if we have three frames. 181 00:08:00,480 --> 00:08:03,410 We want to actually sort of do it plus one so 182 00:08:03,410 --> 00:08:05,160 that we go all the way to the third frame, 183 00:08:05,160 --> 00:08:09,765 and then we calculate it as if we are incrementing beyond into the next frame 184 00:08:09,765 --> 00:08:11,640 beyond the edge of the frame table, let's say 185 00:08:11,640 --> 00:08:14,142 frame four if we had three frames. 186 00:08:14,142 --> 00:08:17,100 Because otherwise we would get to frame three and immediately loop back 187 00:08:17,100 --> 00:08:18,618 to frame one and completely skip it. 188 00:08:18,618 --> 00:08:19,660 We don't want to do that. 189 00:08:19,660 --> 00:08:24,120 We want to sort of account for the next frame, the next invisible frame, 190 00:08:24,120 --> 00:08:25,490 to give us that buffer. 191 00:08:25,490 --> 00:08:28,710 And then what we want to do is say if self.currentFrame 192 00:08:28,710 --> 00:08:32,130 is equal to zero, which is what's going to happen when we use modulus, 193 00:08:32,130 --> 00:08:35,650 then self.currentFrame should be equal to one, because, remember, 194 00:08:35,650 --> 00:08:38,730 we should have tables in Lua be one index. 195 00:08:38,730 --> 00:08:43,453 That's just how it works by default. But when you start messing with modulo, 196 00:08:43,453 --> 00:08:45,870 it does go back to zero, so we want to make sure that this 197 00:08:45,870 --> 00:08:47,700 gets set appropriately back to one. 198 00:08:47,700 --> 00:08:51,600 Because there is no zero frame in our table, so this wouldn't work. 199 00:08:51,600 --> 00:08:54,210 And that's basically it for the animation class, 200 00:08:54,210 --> 00:08:55,860 actually, so pretty compact. 201 00:08:55,860 --> 00:08:57,370 A little bit of information there. 202 00:08:57,370 --> 00:08:59,537 But that's it in terms of actually getting something 203 00:08:59,537 --> 00:09:01,480 that will work to animate stuff. 204 00:09:01,480 --> 00:09:03,900 So let's go back to the Player class here. 205 00:09:03,900 --> 00:09:06,450 Now what we want to do is, previously, we 206 00:09:06,450 --> 00:09:09,070 don't have any animation data so we want to start adding that. 207 00:09:09,070 --> 00:09:11,970 So we're going to add a self.animations. 208 00:09:11,970 --> 00:09:13,950 We're going to make that a table. 209 00:09:13,950 --> 00:09:16,530 And we're going to index into two different animations. 210 00:09:16,530 --> 00:09:20,340 So we have an idle animation, which really isn't much of an animation. 211 00:09:20,340 --> 00:09:23,730 And we're going to say that it's an animation with this curly bracket 212 00:09:23,730 --> 00:09:24,540 syntax. 213 00:09:24,540 --> 00:09:26,790 Now, the reason we can use this curly bracket syntax 214 00:09:26,790 --> 00:09:31,140 is because in Lua if the only argument to a function is a table, 215 00:09:31,140 --> 00:09:36,690 then you don't have to use, say, this syntax, which you would instinctively 216 00:09:36,690 --> 00:09:38,970 think to use like that, right? 217 00:09:38,970 --> 00:09:42,360 Because it only takes in a single table as its only argument. 218 00:09:42,360 --> 00:09:45,110 Lua gives you the shorthand to actually get rid of the parentheses 219 00:09:45,110 --> 00:09:45,810 there just like that. 220 00:09:45,810 --> 00:09:46,950 So we're going to use that. 221 00:09:46,950 --> 00:09:50,490 So idle is going to be an animation as will walking. 222 00:09:50,490 --> 00:09:55,830 So again, the idle animation isn't going to be much of an animation, 223 00:09:55,830 --> 00:09:58,260 but it's still capable of being treated like one. 224 00:09:58,260 --> 00:10:02,700 Our animation class does account for any animation that is only one frame long. 225 00:10:02,700 --> 00:10:04,800 So we're going to do that. 226 00:10:04,800 --> 00:10:11,400 And the animation class takes as an argument the table of parameters. 227 00:10:11,400 --> 00:10:13,110 So again, it takes in textures. 228 00:10:13,110 --> 00:10:16,420 So we're going to pass in a self.texture. 229 00:10:16,420 --> 00:10:18,270 It also takes in frames. 230 00:10:18,270 --> 00:10:20,550 So we're going to say frames is going to be equal to. 231 00:10:20,550 --> 00:10:22,320 And then this is another table. 232 00:10:22,320 --> 00:10:24,960 And we're going to specify quads here, which 233 00:10:24,960 --> 00:10:27,240 we've gotten already into self.frames. 234 00:10:27,240 --> 00:10:30,180 And I specifically want to make sure to grab the right frames here. 235 00:10:30,180 --> 00:10:35,700 So I have one, two, three, four, five, six, seven, eight, 236 00:10:35,700 --> 00:10:40,200 it looks like 9, 10, and 11 make like a pretty good walking animation. 237 00:10:40,200 --> 00:10:41,970 So that'll be my walking frames. 238 00:10:41,970 --> 00:10:44,100 And then I only need frame one for my idle frame. 239 00:10:44,100 --> 00:10:46,380 So 9, 10, and 11 for the walking. 240 00:10:46,380 --> 00:10:48,600 And one for the idle animations. 241 00:10:48,600 --> 00:10:54,150 Let's go back here for idle frames is only going to be self.frames one, 242 00:10:54,150 --> 00:10:58,410 but walking is going to be, again, texture equals self.texture frames 243 00:10:58,410 --> 00:11:06,750 being equal to self.frames of 9, 10, and 11. 244 00:11:06,750 --> 00:11:07,320 OK. 245 00:11:07,320 --> 00:11:10,800 And the other thing that these are going to need too is an interval. 246 00:11:10,800 --> 00:11:13,410 Now, the interval for idle doesn't really 247 00:11:13,410 --> 00:11:17,560 matter because it's just a single texture. 248 00:11:17,560 --> 00:11:20,430 And it doesn't actually get used, but we'll say interval of one. 249 00:11:20,430 --> 00:11:22,380 And then for walking, why don't we go ahead 250 00:11:22,380 --> 00:11:27,240 and say that this is going to be an interval of, let's say, 0.15. 251 00:11:27,240 --> 00:11:28,600 That should be good. 252 00:11:28,600 --> 00:11:29,118 Cool. 253 00:11:29,118 --> 00:11:31,410 So another thing that we're going to end up getting to, 254 00:11:31,410 --> 00:11:33,720 and this ties back into the idea of state, 255 00:11:33,720 --> 00:11:37,800 is currently we could just put things into sort of an IF statement. 256 00:11:37,800 --> 00:11:41,770 We could say if the player state is equal to x, then do this. 257 00:11:41,770 --> 00:11:43,740 Otherwise, if it's equal to this, then do this. 258 00:11:43,740 --> 00:11:43,950 You know? 259 00:11:43,950 --> 00:11:46,680 If it's equal to standing, then we'd check for keyboard input 260 00:11:46,680 --> 00:11:47,717 and so on and so forth. 261 00:11:47,717 --> 00:11:49,050 It gets a little bit cumbersome. 262 00:11:49,050 --> 00:11:52,708 And we're not going to save a tremendous amount of work by doing it this way. 263 00:11:52,708 --> 00:11:54,500 But there's an interesting thing we can do. 264 00:11:54,500 --> 00:11:58,260 We can actually model a state machine pretty effectively as 265 00:11:58,260 --> 00:12:04,410 by if we say self.behaviors is equal to a table. 266 00:12:04,410 --> 00:12:07,260 And let's just say we have in idle behavior. 267 00:12:07,260 --> 00:12:10,410 And I'm just going to give it a function like that. 268 00:12:10,410 --> 00:12:13,800 So I'm actually storing a function as a value 269 00:12:13,800 --> 00:12:16,030 inside of this table with the key idle. 270 00:12:16,030 --> 00:12:19,070 And I'm going to do the same thing for walking equals a function. 271 00:12:19,070 --> 00:12:21,070 And this might look a little bit strange to you, 272 00:12:21,070 --> 00:12:23,070 but this is something that you can get with Lua. 273 00:12:23,070 --> 00:12:26,130 It has first class functions, functions that can be treated just 274 00:12:26,130 --> 00:12:28,360 like data, like objects, like strings. 275 00:12:28,360 --> 00:12:29,860 But in this case, they're functions. 276 00:12:29,860 --> 00:12:33,780 And so we can assign them to this key here idle and walking. 277 00:12:33,780 --> 00:12:37,800 And then as by, for example, going into, let's say, 278 00:12:37,800 --> 00:12:42,510 update and saying self.behaviors at self.state, where self.state 279 00:12:42,510 --> 00:12:45,870 is going to be either idle or walking. 280 00:12:45,870 --> 00:12:49,230 We can then just call it like that and pass in delta time 281 00:12:49,230 --> 00:12:50,430 like it were a function. 282 00:12:50,430 --> 00:12:53,790 Because we're returning a function from these keys. 283 00:12:53,790 --> 00:12:55,590 So a pretty cool feature of Lua. 284 00:12:55,590 --> 00:12:59,430 This exists in languages like JavaScript as well and Python to an extent. 285 00:12:59,430 --> 00:13:01,590 But super-cool first class thing. 286 00:13:01,590 --> 00:13:04,390 When you get into functional programming in maybe a later course, 287 00:13:04,390 --> 00:13:05,920 this kind of ties into that as well. 288 00:13:05,920 --> 00:13:07,240 So pretty neat. 289 00:13:07,240 --> 00:13:07,740 OK. 290 00:13:07,740 --> 00:13:10,980 So when we're in the idle state, really what we want to do 291 00:13:10,980 --> 00:13:12,990 is just check to see this right here. 292 00:13:12,990 --> 00:13:17,100 If the keyboard is down, where we're pressing A or D, 293 00:13:17,100 --> 00:13:18,870 we should move left or right. 294 00:13:18,870 --> 00:13:19,800 So we'll do that. 295 00:13:19,800 --> 00:13:22,020 And really, with walking, it's the same thing. 296 00:13:22,020 --> 00:13:25,680 Now, one thing we need to make sure we do is actually keep track of the state, 297 00:13:25,680 --> 00:13:27,190 so self.state. 298 00:13:27,190 --> 00:13:32,190 We'll set this to idle as our default. And then now then by default, 299 00:13:32,190 --> 00:13:34,290 we'll just always be triggering this here. 300 00:13:34,290 --> 00:13:37,950 And if love.keyboard is down A or D, then 301 00:13:37,950 --> 00:13:41,850 our x is going to move minus mu speed times delta time 302 00:13:41,850 --> 00:13:45,240 or plus move speed times delta time, depending on which key we press. 303 00:13:45,240 --> 00:13:46,350 Same thing for walking. 304 00:13:46,350 --> 00:13:47,602 Not much has really changed. 305 00:13:47,602 --> 00:13:49,560 Why don't we, as a sanity check, just make sure 306 00:13:49,560 --> 00:13:51,550 this works before we go too much further. 307 00:13:51,550 --> 00:13:53,760 So it looks like I can move left and right. 308 00:13:53,760 --> 00:13:56,333 And everything is still completely functionally appropriate. 309 00:13:56,333 --> 00:13:57,000 So that's great. 310 00:13:57,000 --> 00:13:58,990 Everything looks fantastic. 311 00:13:58,990 --> 00:14:03,460 Now, we aren't actually considering the animation in our code yet, 312 00:14:03,460 --> 00:14:04,710 but we will very soon. 313 00:14:04,710 --> 00:14:08,070 So what I'm going to do, actually, is in render, we 314 00:14:08,070 --> 00:14:12,392 should be getting the current animation and its current frame. 315 00:14:12,392 --> 00:14:14,850 So really, this is going to be not self.frames one anymore, 316 00:14:14,850 --> 00:14:18,630 but self.animation, which means we need to have that as a field. 317 00:14:18,630 --> 00:14:23,350 And then we need getCurrentFrame like so. 318 00:14:23,350 --> 00:14:23,850 OK? 319 00:14:23,850 --> 00:14:24,600 Perfect. 320 00:14:24,600 --> 00:14:28,652 So let's go up here then, self.animation. 321 00:14:28,652 --> 00:14:30,360 And we're going to say that's going to be 322 00:14:30,360 --> 00:14:33,960 equal to self.animations idle for now. 323 00:14:33,960 --> 00:14:38,580 Now, when we get to the point where we want to walk with our character, 324 00:14:38,580 --> 00:14:40,470 we're going to need to set this to walking, 325 00:14:40,470 --> 00:14:43,030 so that'll be important to do when we're down here. 326 00:14:43,030 --> 00:14:45,720 And we can do that as by going down over here and saying 327 00:14:45,720 --> 00:14:50,650 self.animation is equal to self.animations walking. 328 00:14:50,650 --> 00:14:51,150 OK? 329 00:14:51,150 --> 00:14:55,713 We'll do that here, here, here, and here. 330 00:14:55,713 --> 00:14:56,380 So that's great. 331 00:14:56,380 --> 00:14:58,088 So now, animation is going to be walking. 332 00:14:58,088 --> 00:14:59,740 It's no longer going to be idle. 333 00:14:59,740 --> 00:15:01,490 Let's test this just to make sure we don't 334 00:15:01,490 --> 00:15:03,040 have any bugs as we keep going on. 335 00:15:03,040 --> 00:15:04,190 OK? 336 00:15:04,190 --> 00:15:08,290 Oh, and it looks like we have a frame rendering from the animation, which 337 00:15:08,290 --> 00:15:11,920 is perfect, but we are not actually animating because we're not 338 00:15:11,920 --> 00:15:12,940 updating the animation. 339 00:15:12,940 --> 00:15:14,320 So this is very important. 340 00:15:14,320 --> 00:15:19,390 Part of the update function involves us going to self.animation update 341 00:15:19,390 --> 00:15:20,590 delta time. 342 00:15:20,590 --> 00:15:24,160 Let's go ahead and run that. 343 00:15:24,160 --> 00:15:26,800 And we can indeed see that we are moving. 344 00:15:26,800 --> 00:15:28,720 The character is moving in its animation. 345 00:15:28,720 --> 00:15:31,540 Now, we haven't transitioned out of walking yet when we're idle. 346 00:15:31,540 --> 00:15:33,373 And we also haven't taken into consideration 347 00:15:33,373 --> 00:15:36,703 the direction through which our entity is facing, which is very important. 348 00:15:36,703 --> 00:15:38,620 And this is actually going to get into talking 349 00:15:38,620 --> 00:15:41,470 about origin offsetting of drawing our character too. 350 00:15:41,470 --> 00:15:43,640 So let's go ahead and get into that. 351 00:15:43,640 --> 00:15:50,890 So if we're not moving, if it's the case that we aren't pressing A or D, 352 00:15:50,890 --> 00:15:54,040 then what we should do is say self.animation is 353 00:15:54,040 --> 00:15:58,820 equal to self.animations idle like so. 354 00:15:58,820 --> 00:16:00,350 And we'll do the same thing here. 355 00:16:00,350 --> 00:16:01,850 I'm going to copy this line of code. 356 00:16:01,850 --> 00:16:05,180 357 00:16:05,180 --> 00:16:05,850 Animations idle. 358 00:16:05,850 --> 00:16:06,800 Let's go ahead and rerun that. 359 00:16:06,800 --> 00:16:07,700 Make sure it works. 360 00:16:07,700 --> 00:16:08,540 I'm walking. 361 00:16:08,540 --> 00:16:09,680 And I stand still. 362 00:16:09,680 --> 00:16:10,470 And it's perfect. 363 00:16:10,470 --> 00:16:10,970 OK. 364 00:16:10,970 --> 00:16:11,660 Excellent. 365 00:16:11,660 --> 00:16:12,260 That's great. 366 00:16:12,260 --> 00:16:15,140 Now, we do have a little bit of blurring of the character 367 00:16:15,140 --> 00:16:18,370 because we're drawing our self.x and self.y at fractional values 368 00:16:18,370 --> 00:16:19,140 it looks like. 369 00:16:19,140 --> 00:16:22,490 So I'm just going to call a math.floor on both self.x 370 00:16:22,490 --> 00:16:26,920 and self.y just to get rid of that issue. 371 00:16:26,920 --> 00:16:28,177 Perfect. 372 00:16:28,177 --> 00:16:31,010 And the next thing we need to do is take into consideration the fact 373 00:16:31,010 --> 00:16:32,343 that we are flipping directions. 374 00:16:32,343 --> 00:16:33,950 And this is very important. 375 00:16:33,950 --> 00:16:37,820 Currently, the animation, if we look back at our frames, 376 00:16:37,820 --> 00:16:40,300 the animation is specifically drawn as if the character 377 00:16:40,300 --> 00:16:41,300 is looking to the right. 378 00:16:41,300 --> 00:16:44,300 But it is the case that our character will be facing to the left 379 00:16:44,300 --> 00:16:45,660 if we're moving to the left. 380 00:16:45,660 --> 00:16:47,452 So we need to take this into consideration. 381 00:16:47,452 --> 00:16:50,738 So in order to do this, we're going to have a self.direction. 382 00:16:50,738 --> 00:16:53,030 And this is by default going to be right because that's 383 00:16:53,030 --> 00:16:56,520 the direction of the animation by default. 384 00:16:56,520 --> 00:17:03,200 And if I press A, which is to the left, then I should set self.direction left. 385 00:17:03,200 --> 00:17:04,190 Copy this. 386 00:17:04,190 --> 00:17:07,910 And I'm just going to paste this down here. 387 00:17:07,910 --> 00:17:13,980 And self.direction should be right in the context of pressing D. 388 00:17:13,980 --> 00:17:17,640 So I'm going to do that right here and right here. 389 00:17:17,640 --> 00:17:18,380 Perfect. 390 00:17:18,380 --> 00:17:20,730 So that's not going to do anything in and of itself. 391 00:17:20,730 --> 00:17:23,599 Now what we need to do is determine whether or not 392 00:17:23,599 --> 00:17:24,995 we should flip the animation. 393 00:17:24,995 --> 00:17:29,330 And this is an interesting thing that we can add to love.graphics.draw call. 394 00:17:29,330 --> 00:17:31,490 It takes in a few extra arguments at the end, 395 00:17:31,490 --> 00:17:33,830 which we haven't really explored yet. 396 00:17:33,830 --> 00:17:36,560 And some of those are the rotation, but also 397 00:17:36,560 --> 00:17:39,650 whether it's offset from its origin and whether it's 398 00:17:39,650 --> 00:17:41,910 flipped on one axis or another. 399 00:17:41,910 --> 00:17:45,515 So what we can do is say 01. 400 00:17:45,515 --> 00:17:49,440 Let me just confirm the order of the arguments here. 401 00:17:49,440 --> 00:17:50,600 So if we say zero-- 402 00:17:50,600 --> 00:17:54,695 and that's the initial rotation, which we don't. 403 00:17:54,695 --> 00:17:55,970 We don't want any rotation. 404 00:17:55,970 --> 00:18:00,410 But we do want to flip our sprite on a particular axis. 405 00:18:00,410 --> 00:18:04,820 So what we can do is we can say let's say scale x will be the variable. 406 00:18:04,820 --> 00:18:06,410 So we'll say scale x. 407 00:18:06,410 --> 00:18:09,650 So that's the scale factor really of our sprite. 408 00:18:09,650 --> 00:18:15,110 And actually, if you scale something by a negative number, negative one, 409 00:18:15,110 --> 00:18:16,460 it has the effect of flipping. 410 00:18:16,460 --> 00:18:18,750 Now, normally scaling just means stretching something. 411 00:18:18,750 --> 00:18:21,980 So if I scale something by say two on the x-axis, 412 00:18:21,980 --> 00:18:25,580 it'll make it twice as long, or on the y-axis, twice as tall. 413 00:18:25,580 --> 00:18:27,770 If I scale something by a negative number, 414 00:18:27,770 --> 00:18:30,440 and specifically a negative one, it'll actually 415 00:18:30,440 --> 00:18:33,920 scale it in the opposite direction, but also flip it, such that it's mirrored. 416 00:18:33,920 --> 00:18:36,080 And that's exactly what we want. 417 00:18:36,080 --> 00:18:43,160 And what I want to do is I want to say if self.direction is equal to right, 418 00:18:43,160 --> 00:18:46,230 then else. 419 00:18:46,230 --> 00:18:51,190 And if it is equal to right, then scale, let's say, local scaleX right here. 420 00:18:51,190 --> 00:18:52,070 Let's pre-declare it. 421 00:18:52,070 --> 00:18:56,730 And we'll say scaleX is equal to one. 422 00:18:56,730 --> 00:18:58,920 Otherwise, it's going to be equal to negative one. 423 00:18:58,920 --> 00:19:03,060 So in the case that it's left, our scale factor is going to be negative one. 424 00:19:03,060 --> 00:19:06,560 Now, a weird sort of byproduct of scaling something by negative one 425 00:19:06,560 --> 00:19:09,410 is it also shifts the texture over to that direction. 426 00:19:09,410 --> 00:19:11,600 It actually flips it, but it flips it, and it 427 00:19:11,600 --> 00:19:15,030 makes it such that now it's drawing its relative to its right corner. 428 00:19:15,030 --> 00:19:16,550 We don't want that at all. 429 00:19:16,550 --> 00:19:18,800 What we really want to do is flip some thing such 430 00:19:18,800 --> 00:19:21,500 that it's flipping from its center point. 431 00:19:21,500 --> 00:19:23,900 And normally, that is not the default behavior in love. 432 00:19:23,900 --> 00:19:26,400 Normally, things are relative to the top left corner. 433 00:19:26,400 --> 00:19:28,310 So to flip something by its center point, 434 00:19:28,310 --> 00:19:32,540 we have to essentially move what's called the origin point of our texture. 435 00:19:32,540 --> 00:19:34,490 And normally, the origin point, like I said, 436 00:19:34,490 --> 00:19:37,370 is right up here at the top left corner of the sprite. 437 00:19:37,370 --> 00:19:40,220 We want to set the origin point here because, otherwise, we 438 00:19:40,220 --> 00:19:42,920 would be flipping it relative to its left side here. 439 00:19:42,920 --> 00:19:44,040 We don't want to do that. 440 00:19:44,040 --> 00:19:46,670 We want to flip it such that it stays in place 441 00:19:46,670 --> 00:19:48,810 and has the appearance of changing direction. 442 00:19:48,810 --> 00:19:51,950 So that's what we get by changing the origin by specifically moving it 443 00:19:51,950 --> 00:19:53,870 to the center of the sprite. 444 00:19:53,870 --> 00:19:56,540 So I'm going to say scaleX and one because I 445 00:19:56,540 --> 00:19:57,990 don't want a scaleY factor at all. 446 00:19:57,990 --> 00:20:00,320 I just want to scale it normally based on y. 447 00:20:00,320 --> 00:20:02,990 And here, what I want to do, essentially, 448 00:20:02,990 --> 00:20:08,090 is add the offset of whatever half of its width and height are. 449 00:20:08,090 --> 00:20:16,550 So we'll just say self.width divided by two and self.height divided by two. 450 00:20:16,550 --> 00:20:18,780 So just like that. 451 00:20:18,780 --> 00:20:22,470 So that'll end up being 8 and 10, respectively. 452 00:20:22,470 --> 00:20:24,080 So that takes care of that. 453 00:20:24,080 --> 00:20:27,630 Let me just make sure everything here looks like it should. 454 00:20:27,630 --> 00:20:31,610 Let's run it just to give it a test. 455 00:20:31,610 --> 00:20:34,100 Oh, and I forgot one other thing that's very important. 456 00:20:34,100 --> 00:20:37,773 You need to make sure that you also shift the drawing of the sprite 457 00:20:37,773 --> 00:20:38,690 once that is finished. 458 00:20:38,690 --> 00:20:41,410 Because since it's relative to the center point, 459 00:20:41,410 --> 00:20:43,100 it's drawing it at the center point. 460 00:20:43,100 --> 00:20:46,880 We need to account for this origin shift by actually incrementing. 461 00:20:46,880 --> 00:20:52,080 We're adding to self.x the self.width divided by two and the self.y, 462 00:20:52,080 --> 00:20:55,530 we need to do self.height divided by two, just like that. 463 00:20:55,530 --> 00:20:56,030 OK? 464 00:20:56,030 --> 00:20:56,655 We'll run that. 465 00:20:56,655 --> 00:20:58,040 So now it's back on the ground. 466 00:20:58,040 --> 00:21:00,710 So notice that it looks like I have a slight bug here. 467 00:21:00,710 --> 00:21:04,370 So I am only moving to the left no matter what. 468 00:21:04,370 --> 00:21:07,900 So let me just go ahead and verify that this is appropriate. 469 00:21:07,900 --> 00:21:10,430 Self.direction is equal to right. 470 00:21:10,430 --> 00:21:12,010 OK. 471 00:21:12,010 --> 00:21:13,430 Oh, because all of these are left. 472 00:21:13,430 --> 00:21:13,930 OK. 473 00:21:13,930 --> 00:21:19,100 So if it's A, then it needs to be left, but if it's D, it needs to be right. 474 00:21:19,100 --> 00:21:20,530 And same thing down here. 475 00:21:20,530 --> 00:21:22,800 If it's D, it needs to be right. 476 00:21:22,800 --> 00:21:24,590 Let's go ahead and run this one more time. 477 00:21:24,590 --> 00:21:25,460 Let's move. 478 00:21:25,460 --> 00:21:28,700 And I do indeed see the avatar moving in place, 479 00:21:28,700 --> 00:21:31,850 not flipping and shifting over immediately. 480 00:21:31,850 --> 00:21:33,950 And the animation is running perfectly. 481 00:21:33,950 --> 00:21:37,250 And when I stop moving, our character is idle on the screen. 482 00:21:37,250 --> 00:21:41,500 So that was a lot, but it was very smooth, I think. 483 00:21:41,500 --> 00:21:44,830 Join me in Mario7 as we take a break away from that stuff, 484 00:21:44,830 --> 00:21:46,580 and we do something a little simpler where 485 00:21:46,580 --> 00:21:50,400 we add an ability to jump and, therefore also, the notion of gravity, 486 00:21:50,400 --> 00:21:52,610 which is very important in platforming games. 487 00:21:52,610 --> 00:21:55,240 So see you soon in Mario7. 488 00:21:55,240 --> 00:21:56,733