Curves & Tweening - Roblox Scripting Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in today's video we're going to be learning how to create curves we're going to learn how to Tween the curve we're going to learn how to sync the tween between clients without using any network data we're going to learn how to correct the handle content streaming and we're also going to learn how to do a little bit of object-oriented programming by creating a class in order to create our curves we're going to be using float curves now the way float curves work is we insert float curve Keys into the float curve object and these Keys contain a time value and interpolation mode we have three modes to pick from we have constant linear and cubic which is the default mode one way to imagine float curve keys are like points in a graph so we can see a float curve key takes in two values time and value now if we imagine a graph where time is along the bottom and the value is along the side so for instance we can make a key with a time set to 5 and a value set to 3 which would create a point over here and we could create a different key with a time of 8 and a value of one and we could create a different key with a time of 12 and a value of 5 and now we can use the get value at time at any time we like and it will interpolate using these points to receive a value and the value will look something like this okay so now let's experiment with float curves I've created a base plate project and what I'm going to do is create a local script inside of replicated first and the first thing I'm going to do in this side of this script is create the float curve to create a float curve all we need to do is say local float curve equals instance dot new float curve the next thing I want to do is create a float curve key to do this I'm going to say local float curve key one equals float curvekey.new and the first value is the time I'm going to set the time to 10. and then second value is the value so I'm going to also set this to 10 and the third value is the interpolation mode so I'm going to say enum dot T interpolation mode and we have three options constant linear and cubic now if we leave the last value empty it's going to default to cubic so we don't actually have to set it to cubic so we can just set it like this and so that we have more than one point in our graph I'm going to copy and paste this float key four times and change the name to one two and three and four and I'm going to set the time from 10 to 45 to 60. and the final time is going to be 80. and I'm going to set the values from 10 to go up to 50. and then tip down to 30 and then back up to 40. now we must insert these Keys into the float curve so to do this there are two ways one way is to say float curve insert key and into past the key into this function and we could do this for all four keys like so another option to insert Keys is to use the set Keys function so if I say float curve set Keys now this requires a table of keys so what I'm going to do is create a new table and I'm going to call it float curve keys like so and I'm going to place these Keys into the table like this and now I can insert this table into this set Keys function what I'm going to do now is Loop using a for Loop to Loop through time and then use the float curve to receive a value and then I'm going to create a part and I'm going to use that value as the height of the part so to do this I'm going to say for float time equals 1 to 200 do I'm going to create a part I'm going to set the parts position and I'm going to set the x-axis to float time and for the Y axes I'm going to use the get value at time with the float time and for the z-axis I'm just going to use a fixed value of negative 20. I'm going to Anchor the part I'm going to set the part size to 1. I'm going to set the part shape to a ball and I'm going to parent the part to workspace so that we can see where the float curve keys are what I'm going to do is if the float time is equal to 10 45 60 or 80 I'm going to set the parts color to Green so to do this over here I'm going to say if flight time equals 10 or 45 or 60 or 80 part color equals green and if we now test again we can see that there are 200 parts and at the time of 10 the parts should be a height of 10 and then it's going to curve up and once we get to the time of 45 it's going to have the height of 50 and then at the time of 60 we're going to have the height of 30 and then once we get to the time of 80 it's going to have the height of 40 and all these parts over here should have a height of 40. let's now take a look at the two other properties for float curve keys so there are two more properties called right tangent and left tangent and the tangent to a curve is simply the angle at which the curve goes through a point so here we can see the angle indicated by this red line now if we don't set the left or right tangent for the key Roblox will automatically calculate the tangent for us so here we can see that Roblox has calculated a tangent for this key roughly like this and for this key something that looks something like this and for this key something that looks like this for this key something that looks the black this let's Now set a custom tangent for this third point with a angle of 45 degrees so it's going to go through the point like this so now instead of the curve going down like this it should come down and through the point at a 45 degrees and then up through here like so okay so what I'm going to do now is I'm going to copy and paste all this code down here so that we have two curves and then I'm going to move this top one further back to negative 30. and for this second curve I'm going to set the third key to have a tangent of 40. so all I could say is float curve keys I'm going to get the third index I'm going to say dot now we can set the left or right tangent if we set only one of the tangents then both tangents will be set and later on I'll show you what happens if we set both of the tangents so let's for now just set the left tangent now this value has to be in radii so I'm going to use math dot red and I'm going to set it to 45 degrees and if we now test the game again we just see two curves the top one being at the back and the bottom curve being at the front and if we look at the third point of these two curves we can see that if we overlap them we can see that the back curve goes down through the point set by Roblox and this one over here which we set to 45 degrees comes down and back through the point in a 45 degree angle like this purple line that we set here okay so let's Now set the right tangent of the third point so just like we set the left I'm now going to set the right pangent but instead of setting it to 45 degrees I'm going to set it to negative 45 degrees and if we now test the game again we can see that the left side wants to go through the point at 45 degrees where the right side wants to go through the part at negative 45 degrees which creates a point like this okay so now that we've learned how float curves work let's delete this local script and let's now create a path and tween Parts between each point in the path so the first thing I'm going to do is inside of workspace I'm going to create a new folder and I'm going to name this folder paths and inside of this paths folder I'm going to create a model and inside of this model I'm going to create a part and what I'm going to do with this part is I'm going to set it to a green color I'm going to set the size to 3 3. I'm going to set can Collide to false can query to false can touch to false and I'm going to set anchored to true and I'm also going to set shape to Bull and now I'm going to move this ball slightly forward and up and then I'm going to press Ctrl D to duplicate this bull and I'm going to move it over here I'm going to press Ctrl D once again move this one over here and then I'm going to press Ctrl d one final time and move this one over here and then I'm going to name this ball zero one zero two zero three and zero four let's now create the part that's going to Tween from point to point to point so inside of workspace I'm going to create a new part and for this part I'm going to set the material to Neon and then I'm going to set the size to 2 2. I'm going to set can Collide can query and can touch to false I'm going to set anchored to true and I'm going to set shape to full the next thing I'm going to do is inside of this part I'm going to create an attachment and I'm going to set the position of this attachment to 0 1 0. and then I'm going to create another attachment and I'm going to set this other attachment to a position of zero negative one zero so one attachment is going to be at the top and the other attachment is going to be at the bottom and now inside of the part I'm going to create a trowel and for this trowel I'm going to set Face camera to true I'm going to set light emission to one I'm going to set uh transparency to go from 0.5 all the way up to one and I'm going to set attachment 0 to this top attachment and attachment one to this bottom attachment and then I'm going to set lifetime to 1. and for width I'm going to set it to go from a value of one all the way down to a value of zero and if we now move the part we should see that it has a trowel and what I'm going to do is move this part into replicated storage let's now create our simple path class so inside of replicated storage I'm going to create a module script and I'm going to name this module script path now in order to make this module script act like a class all we need to do is say module dot underscore underscore index equals module and then I'm going to create a function within this module called module dot new equals function and inside of this function we're going to create a variable called self and I'm going to say equals to set meta table and I'm going to create a new table and I'm going to set the meta table to this table to module and finally I'm going to return the self table to whoever called the new function now if you're not too sure how meta tables work I have a video on my channel which will go into more detail into how meta tables work but in short what we're doing is when someone calls the new function we're creating a new variable called self and we're setting this variable to a empty table and we're setting this empty table's meta table to module and module has set its index to itself so what this allows us to do is store shared values within each object for instance if I create a value inside of module to something like this now if we try to access this value key within this table even though this value key does not exist within this table because we've set the meta table to this module table it's still going to be able to find this value over here and this is going to help us save memory so for instance instead of having to create a function for every object that when we call new what we can do is share a function between all objects by simply placing the function inside of this module table so I can create a function over here called set and now we can access this set function using the self table so for instance I can say self and I can call the set function like this and what Lou is going to do is first going to attempt to find the set key within this table and it's not going to be able to find it so it's going to check the meta table and The Meta table is going to tell it to check this table which is itself and then it's going to check for the set key within this table and it's going to find a function over here let's now delete this shared value key because we don't need it and let's also delete this set call because we don't need that as well but let's keep the set function because we're going to use this in the future and now that we've created our class let's create a script which is going to use this class so inside of replicated first I'm going to create a local script and I'm going to name this script paths and inside of this script what I'm going to do is I'm going to require this module script so to do this I'm going to say local path module equals require gain dot replicatedstorage wait for child path and then I'm going to use the module to call the new function which is going to return the self object which is this table over here so to do this I'm going to say local path equals path module dot new and now what I want to happen is when we create a new path I want each path to have a part with a trowel within it so to do this what I'm going to do is I'm going to first get a reference to this part inside of replicated storage and to do this I'm going to say local part equals game dot replicated storage wait for Child part and now that we have a reference I'm going to clone this part and store it within the self table over here so over here I'm going to say self.part equals part dot clone and then I'm going to parent this clone into the workspace so I'm going to say self.part.parent equals workspace the next thing we're going to want to do is create free float curves one for the x-axis one for the Y axes and one for the Z axes so away we can do this is to Simply say self dot curve equals instant stop new now instead of using a float curve we're going to be using a vector-free curve a vector-free curve is simply a container for free float curves and we can extract the free float curves from this container by simply doing local curve X curve Y and curve Z equals self dot curve and we can call the X function the Y function and Z function to extract the free float curves from the vector-free curve now what I'm going to do is I'm going to create an array of positions so an array of vector freeze like this and I'm going to create three random Vector freeze and I want to pass this positions variable to the new function so I'm going to place this over here and I'm going to receive it over here now I can use this array of vector freeze to calculate the curve flow keys for the curve floats so what I'm going to do is I'm going to Loop through each position inside of this positions array so to do this I'm going to say for index position in positions do and now I'm going to use the position to create a key and insert it into the curve X so to do this I'm going to say curve X insert key and I'm going to create a new float curve key and I'm going to use the index now because index is going to start at a value of 1 and I want time to start at zero I'm going to subtract index by 1 so it starts at zero and then I'm going to store the position x value which is going to be this this and this value into the curve X and now I want to do the same for curve wire and curve C so I'm going to copy and paste this two times and I'm going to change this to curve Y and this to curve Z and I'm going to set this to position y so it's going to be this value this value and this value and for this one I'm going to set position Z which is going to be negative 20 for all keys and one last thing I want to do for the new function is before we parent the part into workspace I want to position this part at the first position in the array so to do this I'm going to say self.part position equals positions and I'm going to get the first index from the array so now that we've stored a part and a curve inside of this table let's use them two values inside of our set function and we're going to use the time that we pass into this function to extract the X Y and Z coordinates from the three float curves and then we're going to use them three values to set the parts position so to do this first I'm going to use the curve to extract the three numbers the X Y and Z coordinates so to do this I'm going to say local position array equals we're going to use the curve and we're going to use the get value from time function using the time that we pass into the set function and then I'm going to create a vector-free from this array and set the parts position so to do this I'm going to say self.part.position equals vector3. new and we're going to unpack this array and this array has three numbers within it and we can use them three numbers as the X Y and Z coordinates for the vector 3 which is going to set the position of the part now if we wanted to instead of creating this variable we could pass this function directly into the table unpack like so and this has the exact same result okay so now let's create a for Loop inside of this script and use the set function to constantly set the position of the part so what I'm going to do is say 4 curve time equals now if we remember we set the time to start at zero so we want this curve time to start at zero so this key is going to be set at zero this key is going to be set at a time of one and this key is going to be set at a time of two so we want to go up to two and I'm going to increment one at a time and then what I'm going to do is say path and then I'm going to call the set function and I'm going to set the current time and another thing I want to do is before we Loop to the next Loop so this Loop doesn't finish instantly I'm going to use task.weight and I'm going to wait one second between each set nothing I'm going to do is at the top over here I'm going to also add another task weight and I'm going to wait for around five seconds so that we have enough time to enter the game to see the part moving so if we now enter the game we should see the part at the first position then it moves to the second position and finally it moves to the third position but instead of incrementing time by one each time let's increment Time by 0.1 and see what happens so now we should see that every one second the part increments Through Time by 0.1 and after 10 seconds it will reach the second point and after another 10 seconds it'll reach the final point okay so instead of using a for Loop to set the part within the path let's now use the Run Services heartbeat event to set the path so at the top of the script I'm going to get the Run service and then I'm going to create a loop function so I'm going to say local function Loop and then I'm going to connect this Loop function to the heartbeat event by saying run service dot heartbeat connect Loop and now instead of using this for Loop what I'm going to do is first create a new variable and I'm going to call it curve time and it's going to be set to zero and inside of this loop we're going to use Delta time to increment curve time so I'm going to say curve time plus equals Delta time and then I'm going to set the path and now we can delete this for Loop and if we now test the gain we should see that it takes two seconds to reach to the end of the path one thing to take note of is the distance between these points have no effect on how long it takes to get from one point to the next because we're using a fixed value of zero for this point a time of one for this point and a time of two for this point is always going to take one second to get from one point to the next so even if I set this value to negative 100 it's still going to take one second to get from here to here and then one second to get from here to here so let's now change this so instead of taking one second to get from one point to the next let's use the magnitude between these two vectors to calculate the time instead of index negative one so what I'm going to do is create a new variable call it curve time and it's going to start at a value of zero and then over here if the index is greater than one because I don't want to increment this on the first index only once we get to the second index so I'm going to say if index is greater than 1 then curve time and I'm going to add the magnitude between the current position and the previous position and then instead of using index negative one I'm going to use curve time if we now test the game we should notice that instead of taking two seconds to complete the path we should now be taking a lot longer to get to the end of the path if we zoom into the part we should notice that it moves at roughly one stud per second and that is because let's assume that the distance from the first point to the second point is a hundred studs so that means it's going to take 100 seconds to move 100 stats and if we wanted to increase the speed of the parts we could simply multiply it up to time by the speed so for instance if I want the part to move at 10 studs a second I can multiply a Delta Time by 10 and if we now test this we should now see that the part moves 10 studs per second but let's assume that we wanted every path to have a different speed so by doing it this way every path is going to need its own curve time but let's do it a different way that doesn't require us to have a different curve time for each path so what I'm going to do is instead of setting Delta time times 10 I'm going to set the speed when we create the path so over here I'm going to set the speed to 10. and then I'm going to get the speed variable over here like so and now all we need to do is divide the magnitude by the speed so I'm going to say magnitude divided by speed let me try to explain why I divide in the magnitude by the speed creates the effect that we want so let's imagine that instead of using the value of 10 let's use the value of 2 and let's imagine that the magnitude from the first to the second point is a hundred studs so when we measure the magnitude it's going to return a hundred and then we're going to divide 100 by 2 because we set the speed to 2. so that means it's going to be we're going to add 50 to the curve time so what that means is we're going to be able to move 100 studs in 50 seconds so that means we must be moving at two studs per second and if we set this to 10 we're going to divide 100 by 10 and that's going to allow us move to move at 10 studs per second let's now replace our three Vector freeze with the positions of these green parts that we created earlier one thing that we need to take into consideration is content streaming so if we select workspace and look into the properties we can see that streaming enabled is set to true so what that means is these green Parts can stream to the client at different times depending on how far away they are from the client's character if you want more information on content streaming I have a video on my channel called content streaming which you can find here and this video will go into more detail about content streaming now because we're going to need the position of all these parts in order to create this Ray at the exact same time what we need to do is make sure that these parts stream in all together at the exact same time and not one by one based on how far away is from the character now to do this all we need to do is select the model and here where it says model stream mode we want to change this to Atomic and now this entire model will stream into the client at the exact same time and so that it's easier to test content streaming what I'm going to do is inside of workspace I'm going to change streaming Target radius to 128 and the next thing I'm going to change is inside.stata player I'm going to set the character walk speed to 20. if we now test the game and if we look inside a workspace and inside of paths and we start to walk away from the model we should see that the model with all the parts stream out and when we walk back the model should stream back in with all the parts at the exact same time so in order to detect when a model is streamed in or streamed out we're going to be using the child added and child removed events on the paths folder so the first thing we're going to need to do is wait for the pass folder so we can do this by simply saying pass folder equals workspace wait for child paths and then I'm going to create a new function and this is going to be called child added and then I'm going to connect a event the child added event for this folder to this function so to do this I'm going to say pass folder.childaded connect child added so we connect this function to the child added event next I'm going to create the child removed function so child removed and now let's connect the event to this function so I'm going to say pass folder dot child removed connect the child removed function another thing we need to take into consideration is the model may have streamed into the folder before this path script has run so we need to Loop every model inside this folder before we connect the connection for any models that already exist in the path so to do this all we need to do is say for index child in paths folder and we're going to get all the children and for each child we're going to call the child added function so if any children already exist by the time we run this script it's not going to get missed out and we're still going to call the child added function for that child and now instead of using these effective freeze for the positions what we're going to do is we're going to get the positions from the model when it streams in so I'm going to move this into child edit and I'm going to remove these Vector freeze and now I'm going to Loop the children of the model which are these parts here so to do this I'm going to say for index child in model get children and because the order of get children is not defined so the order of this index value may not be the same as what we've set the names to instead of using index as the key for the positions we're going to use the child's name so to do this and say positions and I'm going to say two number so we're going to convert the name String to a number and I'm going to say child Dot name equals child Dot position and the next problem we have is we're creating the path using the module and we're saving the path locally within the scope of this function but we don't have access to this path inside of the loop and another problem is in the future we may have more models more paths within the paths folder so we don't want to just save One path we want to save many paths so to do this I'm going to create a table so I can say local paths and I'm going to create an empty table and then I'm going to place this path into this table so to do this I'm going to say parts and for the key I'm going to use the model so I could say like this and use the model as the key and so now all we need to do inside of the loop is Loop through this table like this and now we can access the path from within the table and if we now test the game we can see that after five seconds the path is created and the part moves through the green parts that we created and one thing we need to take note is here we can see the part and if we move away from the model so it streams out we can see that the part stays there but the model streams out and when we walk back towards the model we see that now we have two parts so this is happening because when we stream out the model the part is not destroyed but when the model streams back in again the path is created a second time which creates two parts so let's now create a destroyer function for the path class to do this I'm going to create a brand new function called destroy and inside this function we're going to destroy this part that we created up here so let's do this and say self.part destroyer and at the same time I'm also going to destroy this curve instance that we created over here so let's say self dot curve destroy and now inside of the child removed function we're going to use the model to access the path inside of the paths table because remember we use the model as the key so the first thing I'm going to do is call this destroy function which is going to destroy these two instances so let's say paths model Destroyer which is going to call this function and then I'm going to remove this path from the paths table so that it's no longer found within this Loop so I'm going to say paths model equals nil the next thing I want to do is have the path loop back to the start once it reaches the end so in order to do this we're going to use the modulus operator and if you want more information on how the modulus operator works you can check out my video on my channel called modulus operator and this will go into more detail on how this operator works so the first thing we're going to need to do is we need to know how long it's going to take for the path to reach the end so we know when to loop back now we already worked this out and we're saving it within the current time variable but this variable is currently being discarded so let's save this variable into self so I'm going to replace this to self dot time equals zero and then instead of incrementing curve time I'm going to increment the self time and I'm also going to use self.time over here and now we can access this very variable over here so what I'm going to do now is create a new variable over here called Loop time equals and it's going to be equal to curve time but we're going to use the modulus operator with the time variable inside of this path object so I'm going to say path dot time and then I'm going to replace curve time with Loop time and finally because this path is going to be constantly moving now we no longer need to wait five seconds so we can move this task weight and if we now test the game we should see that once the part gets to the last part it Loops back to the start and does it over again the next thing I want to do is sync the movement of all the paths between all players and currently because we're using curved time and curve time is set to zero when the player enters the game because this value is not the same on all players the position of the paths will not be the same for all players so what we can do is delete curve time like so and over here I'm going to replace curve time with a new variable called server time and we're going to use the get server time Now function which will be in sync between all clients and we can now delete this and instead of using curve time I'm going to replace it with the server time so that we have more than one path I'm going to duplicate this model and I'm going to move this over here and I'm going to adjust the points of this path to be unique from the first path we made so to set to something like this I'm also going to duplicate the fourth point and I'm going to rename it to 0 5 so it gets placed in the correct key for the positions array and I'm going to move this over here like so if we now go to the test Tab and start a local server with two players what we should notice is on the server side there is no parts moving and if we go to view and network we can see that the overall income in and outgoing data is almost zero so the parts movement have no effect on network data and if we take a look at both clients we can see that the parts move in sync between both clients and if one of the clients walks away and streams out one of the models now we can see that this client on the left is only animating one model while this client on the right is animating two models and if this client walks back into range and the model gets streamed back in the part is back in sync between the two clients let's now use the tween service and use the get value function to Tween our paths so the way the get value function works is we provide a number between 0 and 1 and a ease in Star and an ease in Direction and it's going to return a number back to us for instance if we choose the bounce ease and star and if we passed in the alpha of 0.5 what it's going to do it's going to go up and where it intersects with this line it's going to return this value so 0.5 will return a value of roughly around 0.25 or for instance if we passed in the value of 0.75 if we go up into six around here so this is going to be around 0.6 so let's now create a new function for our class and let's call it tween so over here I'm going to create a new function called tween and just like the set function it's going to receive a time but unlike the set function it's going to receive a easing style and a ease in Direction the first thing I'm going to do inside of this tween function is calculate the alpha to get the alpha all we're going to do is divide the time by the total time and we can do this by simply saying local Alpha equals curve time divided by self dot time another way to think about Alpha is like a percentage where zero is zero percent and one is a hundred percent and now let's use the get value function inside of the tween service so the first thing I'm going to do is get the tween service by saying local tween service equals game get service tween service and now I'm going to use the alpha ease install and ease in direction to get the tween value so to do this I'm going to say local tween equals tween service get value I'm going to pass in the alpha the ease and start and the ease in Direction and now that we have the tween value we're going to expand this out the same way we did for Alpha but in the opposite direction so here we can see that we're converting time into a normalized value between 0 and 1 and now we need to do it in the opposite direction to expand it back to be based on the total time of the path so to do this I'm going to say curve time equals tween multiplied instead of divide we're going to do the opposite of multiply by itself dot time and now that we've calculated the tweened curve time we're going to do the same as set so I'm just going to copy and paste this over here like this but if we wanted to we can compress our code slightly by moving this directly into the function and now I no longer need to update Tween time and just pass the value directly into the get value at time function let's now replace the set function with the tween function so over here I'm going to change this to Tween and we're going to use the same Loop time for the curve time then we need to pass in the ease install so let's go enum dot ease install and I'm going to use bounce and then for the ease in direction we're going to go enum dot ease in Direction and I'm going to use out and if we now test the game we should see that when the part gets to the end instead of just instantly teleporting back it now has a bounce effect before it teleports back and as an example we can set the ease and style to exponential and the ease and direction to In and Out and now we should see that the part starts off slow speeds up as it gets to the center and then slows down once it gets to the end thank you for watching my video and if you have any questions feel free to leave a comment down below
Info
Channel: Suphi Kaner
Views: 29,985
Rating: undefined out of 5
Keywords: Roblox, Scripting, Tutorial, Curves, Tweening, Syncing, Class, Content Streaming, GetServerTimeNow
Id: OAZvYqbzOWI
Channel Id: undefined
Length: 47min 13sec (2833 seconds)
Published: Tue Aug 29 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.