Hello welcome to the third video in this series where we’re making minecraft using python and uh that’s with the asena engine coded by peta amland and yes we’re in tutorial 3 and hello i put the the video of my face on sorry if that ruins everything But i’ll see if uh sometimes it’s more helpful for me to be able to explain things with my hands and my face so there we go right what we’re doing this video we are zero corrections from the previous video um then number one i’m going to be able i’m going to show You how like you can move um a mob’s um like face and body to like turn and like look at you and so beginning to do something like ai and um looking at like non-player characters interacting with us so getting animals moving and things like that um i want to look at the Colors of the terrain that we’ve generated um oh yeah with this little nmap um mapping function so that we can get like the higher the terrain is um say a certain color and then add some random in so we’ve got some nice uh colors over our terrain and i use the random range Function in there and then i want to do some building and like mining or at least deleting the blocks that we’ve created and then changing the the block type so that’s what i want to aim to do in this video right so first let’s just correct what happened last video so I’m just going to press play and remind myself what we got to so it’s very jittery and i’ve fallen already through the terrain and the terrain is at least being generated so i’ll press escape to come out of that so i did put a comment in video two of what i forgot To do as i was hurrying to finish off in the last video and that is around like line 4243 again if you’re not using your own code you just want to use my code i’ve got a link in the description for that so this code As it is now is in the previous video obviously and when we change all of this i’ll put the a link to uh what we uh what we what we do with the code in this in this video as well so on this line we’re saying basically if half a second Has passed go and generate a new subset so a new line of the terrain what i then forgot to do was to reset what the previous time is so basically what’s going to happen is every update is trying to generate a new subset and they’re kind Of getting in the way of each other and that’s why all that glitched out all that kind of white white is slowing down a lot so what we now want to say is previous time equals now and to get the present time you just write time dot time so then next update loop It’s going to say if now minus previous time is greater than half a second which it won’t be because it’s the updates are happening like 60 times a second something like that even higher sometimes um so it’s going to have to wait until it generates a new subset so I’ve saved the file and let’s just see if that stops us glitching so much yes so now we’ve got our 60 frames per second and we’re generating the subsets lines of the terrain as we expected every half a second so my frame rates now drop to 27 and that’s maybe because perhaps we’re Trying to generate too long um a line in our terrain so maybe we want to do less a subset being smaller the smaller we generate each subset the quicker we can kind of generate them and the less of a jolt will happen although perhaps depending on how much Ram you’ve got um you’ll see less of a job but anyway when they’re finished i’ll just stay in the game for the moment when they’re finished so we’ll have like i think we’re doing 100 by 100 i can’t remember um then we’re going to combine all the subsets And so now there’ll only be like one object so we should get our 60 frames per second back again and everything would be lovely so while that’s happening let me just have some tea good morning by the way i’m recording this in the morning in england And it’s a little cloudy but we should have some sun so now it’s combined i’m actually saying i’m actually looking at 30 frames per second oh dropped to 9 what’s going on it feels nice and smooth i think what might be happening is another little mistake That i’ve made so i’m loving it it is very smooth though and it is now i just want to go and like play this i just want to like jump around and explore things and just while i’m talking about this i think the key thing about minecraft or Games like it’s being able to have a really large terrain where you can just go and explore and like the mathematics that’s procedurally generating that noise function the purlin noise function is truly like mysterious you can go and discover new things that no one’s ever seen before because of the combination of Your seed and then other things we might do in the terrain so later on in the series we’ll we’ll have fun like making biomes and things anyway let’s go and sort out that second mistake so that’s the first one i forgot to do the key thing there To make the the timing work um the other thing is yeah i just noticed i haven’t done my indentation right here where we’re updating um to see whether our subject our player has moved more than one unit only then do we generate a shell but because i haven’t put it on the correct Indent that was also happening every um every frame i believe so that may be slowing things down just to test let’s make our terrain smaller so yeah we did have a hundred by a hundred so is that ten thousand blocks so it’s quite a large terrain running at A good speed so we’ll just do ten by ten and yeah what we could do if we still wanted 100 the width of our train to be 100 or 400 or whatever you want you could then make the subset width smaller than that so maybe divided by uh 10 So it’s then only generating 10 of them at a time actually let’s just go and have a look at what happens there it should mean there’s less whoops what have i done there um type error float object cannot be interrupted as an integer so what that means is that We’re using subwidth i believe in a for loop yeah there we are and that needs to be an integer so what we could do is just say cast that as an integer so put some parentheses around it say int that makes it an integer and that’s the Kind of thing you do if you wanted to print something out to the console and you use the print function and then you want to put a number in there but it won’t work you would then use str to make it into a string and then Then you’ll be able to print your your integer or your flow anyway as an aside so i’ve saved the file again let’s just see if this works now oh no subwidth is still being interpreted as an integer somewhere line 73 so i’m also using it yep here line 73 So i don’t want to keep having to cast it as an integer up here what i should do is cast it here and then i only have to do it once and i don’t have to keep going chain changing around my code later on so that would be the better Way to do it now it should run let’s have a look yes so we’ve got mr goose oh that is noticeably smoother 69 frames per second 69 right um i seem to be stuck for some reason oh that’s because i was pressing e i wasn’t pressing w Oh this reminds me of something remind me in the comments later on if i don’t get to this but i wrote some code um so that when we’re walking forward we can step up instead of having to jump and it’s i don’t know i found it much more Enjoyable to move around my different terrains so if i don’t get to that in in like the next video or something let me know that’s using raycasts to negotiate the terrain um so yeah so it’s now generating this terrain more slowly because we’re only doing 10 by 10 but it’s still working which is nice it’s still filling up the the terrain as we want it um although i’ve still got that 30 frames per second it should be 60 i can’t remember what i’m doing wrong there’s something else that i’m not doing anyway let’s um Put the terrain width down by 10 so it generates a lot faster also we can actually ask it to generate a lot faster so let’s make it faster by two factors so that’s uh very fast let’s just see if we can handle that yeah this is generating And it’s done oh yeah and we’re only doing a ten by ten terrain okay my um frame rate seems to be back up no it’s going dropping down again so either my recording is doing that or i’ll discover it no it’s back up to 60. okay so let’s just uh Make the terrain with the hundred i just forgot that i uh moved it back to 10. let’s just see how quickly look at that that’s really cool it’s generating a lot faster although we’re a little bit joggery so maybe 10 um 10 cubes at a time is a Bit too much for my computer anyway but it’s still playable at the moment while it’s generating um so we maybe want to do about four uh a sub width of about four and then it will be fine anyway right so let let’s have a look at our list corrections times two so yeah We um we’ve corrected those which was generate the shell only after we’ve moved one uh one unit so that’s just an indent and then this was a big mistake i forgot to reset um like a time stamp the last time that we generated a subset so that it was indeed happening um Uh after whatever duration we put here or interval we put here so i might just put 0.05 so we get slightly better performance there terrain width um i’ll keep at 100 for now um and then how many how big is our shell i think we can reduce that Shell width six so it is very small let’s just put it down to three so it’s incredibly small and the other thing i wanted to do was so basically sorry just to explain what i was doing there so the shell if you haven’t seen the other videos or if you’ve forgotten is the The area with an actual collider so an entity entities with a collider that’s derived from the same noise function so it’s it’s syncing with the terrain that we can actually see and it’s just moving with the subject so that we’ve always got like a platform to stand on And i’ve made that even smaller so instead of six by six so 36 cubes now i made it by three by three so nine cubes and so that’s nine is smaller than 36 so it’s even faster for the computer to kind of compute although it’s kind of a small difference so You probably won’t get too much of a of an improvement but improvement isn’t improving um so the last thing we wanted to do at the top this first thing is right create a safety net so if we’re performing um large operations like combining all of those subsets together so that’s Involving all these vertices all these corners and triangles that make up the 3d mesh that’s making the computer kind of like um uh kind of like lag and when that lag happens that can interfere with the physics that’s going on which means we might glitch through the Terrain and if that was happening uh many times what we could do is maybe set up some input so we press a button and that brings us back to the height of the terrain or we could just ask the following question i think this Is the way i’m going to do it so in the update loop in the update function i’m going to say if um the subject’s y position is less than let’s say minus 10 or minus the amplitude so the amplitude is the maximum height and therefore perhaps the maximum depth Or lowness lowest point that our purlin terrain might generate so if we detected we’ve actually fallen through let’s just add one to that just to be safe so if our position has got that low then we need to kind of move the subject back up to the top where they should be Then we’re going to say um subjects y equals and now we want to know you know what um height to be at i’m using the the um purlin noise um formula which is here so i’m just going to copy and paste that I’ve um where i was am i up here there we are so my y uh my subject’s y wants to be noise at their x and that is z so x and z we were using for like block positions in a grid now we want to refer to the the subjects X and y and because we’re in parentheses here i can put sorry my helpful notes are getting in the way i could um go over to the next line as long as you’re in parentheses you can do that in python so i need to turn off those like helpful hints don’t i I don’t know how to do it so i won’t do that on camera because i’ll just oh i could pause the video and then try and switch them up subject said i might do that later if it gets too annoying right so that should go and work out whatever position the subject Is at um move them up to where they should be and actually we should maybe go one square like one unit up well no we need to add the subject’s height so remember our subject is an asina prefab specifically we are the uh first person controller that i think peta ameland Coded this it’s a prefab and so it has one of its um properties like cursor or gravity or its x and y position one of its properties is the height and i think that’s set to two so it’s like two meters so then it will start about six foot four Something like that maybe a bit shorter um anyway so we’ve got our subject positioned there and i am going to add another another i’ll just put plus one at the end there we go so that should kind of drop us back onto the top of the um The terrain but then what i’ve noticed when when i did that um sometimes the speed that we were dropping at is still applied to the player so we kind of like slam into the into the train if we’re lucky if we’re unlucky we just glitch through again and the gravity the Acceleration is just um uh what’s the word accruing getting bigger and bigger so it just keeps glitching us through so there is a function or a method for the the first person prefab controller which is called land and that basically says okay now we’re grounded And take off all of your your speed so actually maybe i’ll take off the maybe i’ll take off the one and see if that that’s fine anyway so this is our safety net um let’s just label that let’s just comment that safety net in case of glitching through terrain smiley face There we go right so that’s our little hack in case we glitch through it’s not a proper solution proper solution should be when we’re combining all the the subsets freeze the player or something like that anyway that’s what i’m gonna do if you’ve got a better idea or you manage To actually solve it put the code in the comments that’d be helpful for me and for other people right that will do us for now so next thing on the list we’ve done this one tick ah look at target so i’m canceling the x rotation of our mob so down the bottom Yeah we’ve got vincent who’s our giant chicken and he’s kind of buried a little bit isn’t he in the train at the moment so let’s go to a different terrain shall we just for now so where’s our purlin noise and then we might be able to see him a bit more easily So we’ll put the amplitude up to i don’t know 24 we’ll keep the frequency at that octaves maybe up to four um do we still want to be 20 21 maybe we’ll go to 99 what does that look like so our seed is 99 octaves four amplitude is quite small Frequency 100. remember the frequency kind of smooths things out um and we’ll be happy with this if we can kind of see vincent so there he is and what we want to do oh i just fixed my terrain and now i’m glitching through already although i didn’t fall far enough um okay So yeah vincent looks like he’s going to be very visible so what would be good is if we could get vincent to kind of turn his head and look at us um like i showed you with a creeper in the first video of this series okay let’s do that Right so in the update loop in the update function we’ll do this under our safety net we’ll we’ll just get hold of vincent there he is and we can just use this inbuilt function from uh cena so again coded by petramland of look at and that just means could you turn The uh the entity vincent um towards another entity and that’ll be us the subject so here we go um yeah we’ll say um subject in the first parameter and then we need what axis um to use and that means you know do we want his The back of his head pointed at him or like maybe his feet pointed at us we want uh yeah the forward axis so his face to point us one problem might be certain models that you’ll get will be kind of like flipped over and things so their forward won’t actually be the Forward that you’re interested in um and i don’t know if we can do this can we just times that by time delta time and then maybe by a scalar to make it happen more slowly we’ll see if that’s right anyway this time delta time means move this or do this function Do this like what’s it called a kinematic movement that means like moving things without the physics engine in 3d space at a rate um that’s relative to how fast the update loop is moving so how many frames per second you’ve got and that means when you’re playing this on a different computer And maybe it’s a slower frame rate this time delta time takes that into consideration and makes it the same rate on that other device so that’s that’s what you should apply to anything that’s kinematic so let’s see if this is already working or breaks um our code Yeah i can’t apply the delta time there so i’ve got to apply that um maybe as another argument later on anyway we’ll just see if this is working first of all have some more tea okay definitely working [Laughter] we’ve now got a giant goose just staring at us and as we move Around he is continuing to look at us brilliant so that’s how you can get your mobs looking at us um now it’s kind of cool if you’ve got a mob who’s in the air this shouldn’t be in the air but maybe you’ve got a ghast or something Like that that’s floating around i don’t know then this is fine perfect um if you’ve got a grounded creature then it means that their whole body will kind of like tilt into the floor and we don’t want that so we kind of want to take out that x Rotation we want to keep that y rotation because that’s turning around but we want to Zero out the x rotation so after we’ve used the the lookout function we can then say vincent vincent can your rotation on the x-axis be cancelled out and then he’ll just stand up straight and look at us let’s move vincent down so in fact he’s not flying so after we’ve created vincent we Create him 7.1 units in the air so let’s try about two i’m not sure this might put him further into the ground as long as we can see his face we’re happy we can see his face yeah now he’s looking at us but he’s uh and i’m going to go into Some hot i’ve got the high ground now goose man i’ve got the high ground watch out now he’s uh yeah so not leaning into the into the ground or anything like that and i maybe need to lift him by two meters one or two meters brilliant Okay it kind of looked better didn’t it because of his size actually leaning forward so um useful for me or useful fit for you the um cancelling out the x uh rotation but i might just comment it out now oh no i’m using like javascript or c there we go python Is to comment out a line right so we’ve done number two we’ve done the uh the x rotation cancelling out and looking at the target um but i didn’t what maybe need to look at is slowing it down if you want it to go slower and things like that Um okay right colors with nmap and random range so we want to apply colors to our terrain and we can do that for each individual um block what did i call it each individual sub cube and that won’t be removed even when we combine each of those cubes into a subset and Then combine the subsets into the the whole terrain that coloration will remain so we want to do that when we’re actually generating the subset um so in yeah in generate subset what we’re then doing is going over every sub cube in the in the like the sub Set area we’re determining its x and its z location and then the crucial thing is we are determining its height and that’s how we get our terrain um then we’re saying its parent is the current subset and that will mean that we can combine it into the subset to to save performance And then we’re just setting its color to green here and then we’re saying it’s no longer visible so that that green and its own texture or if texture will be white cube by default i think won’t interfere with the the overall terrains texture so that’s important but even though we’re setting It to in invisible we can still set its color so you’ll notice that this y value is slightly differently colored in my text editor and that’s because i’m not actually using it but i still wanted to create it because i want to use the height the y value to determine the color So our color is going to be let’s just say for now color dot rgb so red green blue and we’ll just say um a red amount a green amount and a blue amount and so before we do that let me just add a set color of sub cube uh big smiley face because Colors are fun and then we want to create r equals zero uh green equals zero blue equals zero for now so it will be black at the moment and then what we want to do is something like green equals like y so the amount of greenness is the height of our Um cube let’s just go and have a look at that at the moment you can kind of imagine what it might look like oh well it already looks pretty good i didn’t expect it to um take off the already take off the um texture of our of our like sub set So i think because we’ve changed the color and that’s removed the uh subset texture but when we combine the whole terrain we should have um our overall terrains texture reapplied but with the uh kind of like a height map underneath i’m a bit confused why it’s kind of like dark Green up here and light green down there weird well i know there’s fog as well again i should have made the terrain a lot smaller so this loads more quickly during testing just enjoying the view oh another note at least it was very playable while we’re even while we’re generating Terrain another thing i could have mentioned was that um i’m just making like minecraft in it um on the side as well not in in tutorials yet but i found a way to generate an infinite terrain on the fly so similar to what we were doing with the shell oh that’s disappointing it’s Um i’ve now broken it the uh the um texture hasn’t been applied so let’s go and sort that out but at least we’ve got different um shades of green although we can’t really see the difference very well and that’s because our terrain is kind of going from maybe minus The amplitude whatever that was to plus the amplitude um but that might not really fit in terms of this component of the color rgb value we want to go from maybe more 0 to 255 but maybe go from 80 to 255 so they’re not they’re not fully Uh black um and so we need to kind of map the the range of our terrain so that deter as determined by the amplitude to the range that we want for that green color component of the rgb value which will go from as i said maybe 80 to 255 So we’ve got to map those two together and there’s a function that i’ll show you um that can do that um but yeah let’s do that first and then we need to go and sort out what’s gone wrong with our um with our uh textures here right Okay so the map function i think i’ve got the map function um included in my prepared code ah here it is yes so let me just go and copy and paste this code i’m not gonna be able to remember this so i’ll just go through it right so you can put this Maybe actually what would be better is to include this as its own file so let’s do this properly so i’m going to create a new file select my language as uh python there we go paste that in there and then save your text file in the same Folder that we’re in so here i’m in my tutorials folder i’ve got tutorial one two and three so that’s what i’m i’m currently writing on uh running so we need to place this file in the same same folder or within a folder um i don’t know maybe just put it in the Same folder to be there and let’s call it um maybe nmap dot py so we’re writing a module in other words called nmap.py save that and now back in our um main module minecraft module we need to um let’s just say from nmap which is the file i just uh We just wrote um import nmap there we go because our function there is called nmap there we go and let’s save that right now let me just show you the um the function itself so what this does this function it takes in a value and then it maps it to Um the min and max so the last two arguments of another range so we can take the y value of our terrain and we can say well that goes from minus amplitude to plus amplitude and we want to map it to the color range that we want and i Said that’s about from 80 to 255 and then it will do this little bit of maths which is like the original value minus your first minimum divided by the maximum one or the difference between the maximum one and maximum two multiply that up by the difference between Maximum two and minimum two add on minimum two and then that maps the two values so that’s how a map function um works there’s probably one built into numpy or math or something that i just didn’t discover so i think i’ve imported it correctly here i think i can use the same name Let’s just go and see if not we’ve learned something new right go into yeah generate subset and here where i’m determining the r value the green value the blue value and now we want to say actually the green value equals um nmap take the the height of our block And that can go from minus amp to amplitude and we want to go between maybe about 80 to 255 is like the brightest green and so that value then will be plugged into our rgb okay right the other thing we wanted to sort out was the texture And um you can probably see by my face i’m not 100 sure what was happening so the only thing we changed was we’ve set the color to an rgb value instead of what did we have a built-in green value And for some reason that um overrode the texture yeah the grass stroke texture up here so maybe what we need to do let’s try oh look i’ve got application pauses and resumes oh yeah that was my first hack to try and pause the app um to stop glitching through the terrain But i don’t think this works so we can take that out apologies um oh yeah and i’m also yes setting the height of our subject to 32 so that’s kind of disrupting our safety net so i need to delete that so it’s a good job i came over here There we go let’s just see what’s happening in the finished terrain um if the terrain is finished then it returns so it’s not trying to combine all of the subsets over and over again then it combines and destroys the subsets um and then it’s setting the texture of Our terrain although that’s not working for some reason so i need to maybe i need to set this text just somewhere else and i’m i don’t know where let’s have a look let’s try auto destroys false let’s just try setting the texture i don’t know before we combine All the subsets which are children of the terrain into the trains one mesh right at the very least we’re here to see our whether our color mapping has kind of like worked oh it’s worked that’s way isn’t it i don’t really know why that’s worked but it has already so We can see lower down we’ve got fairly dark green and it looks like the higher values are greener but maybe that’s just the uh just an effect of the fog you know what they don’t seem that much brighter do they so i think we need to go our lookout function is definitely working So i think we need to go and oh i know what’s also happening the texture itself is green i think we’re just looking at the texture there isn’t a difference the only variation in the the color we’re getting in our terrain is from the the fog so Oh good news is we’ve got vincent standing on the ground so that’s one thing i achieved okay so first thing i need to go and do is um change the size of our terrain so it’s only like 20 or something um so that we can do the testing a lot quicker Also i think our map function was working because we didn’t and we’ve imported it we didn’t get any errors there that’s the other place where the bug could be maybe maybe that wasn’t working correctly but i think it’s more to do with this texture grass stroke text so What we need actually is a white text or a monochrome texture and i can’t remember if i did that last time so that we can set the color of things and then as long as the texture is white the color that we set the entity to kind of like shine through that texture Which is really handy so uh where am i making my textures oh so we’ve only got one texture which is our grass texture so let me just load up um my assets so what i’ve already looks like i’ve already prepared for this video at some point i’ve got two other textures here i’ve Got grass mono so you can see it’s monochrome and then i’ve got stroke mono and the only difference is stroke is this white line around this one although there’s a white border on here that’s just in my operating system just on the mac the actual texture doesn’t have a white border But this texture does and so maybe we’ll we’ll use that one for now so that’s called stroke mono so again i’ve put these in the assets folder which is in the same folder as where you’re running the code from and where you put the nmap module file as well so as long as They’re all together they’ll all be able to communicate and work um fine so uh i’ve got the name stroke mono let’s go and make that texture so we’ll just call it mono mono text equals load underscore texture stroke mono dot png now i wonder if i just set the subsets terrain to that Will that sort out the color issue we will be able to see different colors at different heights these do look a different color but i don’t see any sorry if this is really noisy there’s planes going over overhead wow that sounds really noisy i’m actually vibrating oh there it is It’s not a plane it’s a it’s a helicopter in case you wanted to know right so i’ve still not got any variation going on and i think that’s to do with where we’re applying the textures so Let’s let’s just comment that out so our sub cube hasn’t got texture and are we getting lighter green at the top i think we are we could see yeah definitely whether the combined function there worked let’s just stretch this right out so maybe we’ll go from zero To amp and then go from zero to two five five so that should increase the variation we should go from black to to green oh there we go in fact i didn’t i didn’t do that too skillfully okay so we’re definitely seeing green at the top so our map function is working And yes we’ve got the texture now and definitely the texture is working now in combination with um the color so i just need to fiddle this i need to be a bit more intelligent here so i want to go from actually the naught to 255 was kind of cool Um but we want to go yeah we kind of want to maximize the difference here don’t we um and minimize this diff distance so we want to go from maybe i don’t know three well it’s gotta be it’s gotta be like a minus that we’re gonna go from like zero maybe To amp minus um amp divided by two okay kind of dark in the low in the low lands what’s gonna happen when we climb okay yeah now we’re getting a bit more more bright at the top lovely cool and it actually looks like there is no texture on the lower ones Because they’re so dark which in effect you might want um so you can also have like different colors so we could say um We could say like for the the blue maybe we could say if y is greater than i don’t know greater than amp divided by 2.5 or three then um blue blue component and just for an example i’m just going to set it to a number but Again you could use the map function to have some more sophistication about this i’m just going to say 255 so get lots of blue We could also say if if y is i don’t know four then r equals uh two five five actually all of them red green and blue all equal to five fives that mean we get white and then i need to just say else map the green To uh to the current height so you should have like a white band going around certain things local variable b referenced before assignment oh right so i’ve only just made it there so i need to say uh b equals zero as well i’m really excited to see the white band What if it doesn’t appear oh there it is so this is a way you could maybe get snow if you put that at um at maybe the top of your mountain and then you could maybe apply a different texture um as well although that’s going to get Tricky i don’t know like playing around with the textures is going to get tricky but um because we’re using just one texture over the entire um terrain maybe um maybe you’d have to not destroy all the blocks but we’re gonna have to oh maybe if you yeah maybe if you just Set the texture and the color through the sub block and then didn’t have a texture for the terrain and didn’t have a texture for the at the subset and the terrain then maybe that would work um again oh i’ve run out of disk space so i’m gonna have to stop the video oh No before we’re gonna do mining and things so i’ll have to do mining in the next video damn i’m really sorry um i’ll see if i can make that today and maybe release it very very soon but there we go this is how we can get different textures And colors on our tray thank you very much for watching i’ll see you next time again leave any comments if you have any suggestions or requests goodbye Video Information
This video, titled ‘Minecraft in Python: set colour of terrain, mobs look at player (with Ursina) – part 3’, was uploaded by Red Hen dev on 2021-06-26 10:30:49. It has garnered 4112 views and 109 likes. The duration of the video is 00:47:07 or 2827 seconds.
In this tutorial we correct some of the code from the previous video, preventing glitches and improving performance of the procedural terrain.
Then we use the look_at() function (from Ursina) to have Vincent, our giant goose, turn and look at us. Finally, we use a value mapping function to set the colour of the terrain according to height, by using a monochrome texture where the colour of each cube/voxel on our terrain can ‘shine’ through.
Code: https://github.com/RedHenDev/ursina_tutorials/tree/main/python_minecraft_tut_2021
Thanks for watching! 🙂