Automating Sequences via Lua Coroutines in C++

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello and this is a video about how you can use lua co routines to automate things in your games and by things i mean cut sequences moving platforms tutorials user interface dialogue sessions that sort of thing in a way this is a continuation of my first video which was embedding lure in c-plus plus and you can watch that video uh from the link below and i do recommend that you watch it before seeing this one i'm outside today because the heat has driven me back into my primal form as a metalhead i can't be inside today it's just too hot so let's just get on with it i'm going to start this project with a skeleton pixel game engine application that already loads the lua virtual machine here we can see we're including the necessary files for lua here is the function we created in the first part of this series check lure which is a convenience function to check for error messages when we do calls to the lua virtual machine and here is the class overriding the pixel game engine already i've created a instance of lure called script i've loaded up some graphics here which is just a simple tile set to make things a bit more visually pleasing i load that graphic in on user create and in on user create i also define my lua virtual machine and give it the libraries necessary in this case i'm giving it all of the libraries i then look for a file called automation1.lua which is the script file we'll be using for the start of this video and that's all it does it's a 512x480 pixel game engine where each in-game pixel is two by two screen pixels and if you're looking at this and thinking whoa whoa whoa slow down a little bit javid then i do urge you to go and watch the video embedding lua in c-plus plus part one it covers all of the basic syntax and the methods required to transact information between a lure virtual machine and a c plus plus environment but if you can't be bothered doing that don't worry i think lua is semantically simple enough and self-describing that you should be able to follow this video if you've got a basic level of programming knowledge now in that video we used lu in a very simplistic form it was essentially just a glorified configuration file and the transactions we did between lua and the c-plus plus environments were quite minimal now because the first video was so simple i thought it might be useful to show a lua script loading up a tile-based level so i'm going to open up my automation1.lua file and start coding some lure i'm going to create a function called load level and this function takes two arguments the first is host and the second is going to be an identifier for a specific level in your game this host variable is quite interesting you may notice that this function load level doesn't necessarily return anything in fact it's going to call the host application in c plus to construct the level on lua's behalf as with most things in lua there are a million and one ways to skin a cat and so i'll reuse the disclaimer i made last time but this is the way i do things it's not necessarily the best way or the most optimal way but i find it a way that's easy to debug and easy to extend the way that lua is implemented in c imposes some quirkiness regarding how we can call functions in the host environment but just bear with me for a few moments whilst we work through this and the nature of this host variable should become self-explanatory i'm going to create two variables map which is going to be a string and size which is a lure table containing a width and a height don't forget in lua pretty much everything is a key value pair so here we're effectively describing something analogous to a struct then i'm going to check the value of the level parameter that we pass in and respond by creating an appropriate map for that level now this is how i do things and this isn't necessarily the best way to represent level information i'm using ascii here because it's convenient and flexible enough for me to make this video typically in level design and layouts you will want a more sophisticated representation for your level but in effect all i have are tiles that exist and tiles that don't you've seen me use this approach in countless videos before one of the nice things about this being lua is you have access to all of the additional lua libraries and functions so you could open up additional files you could read from them and parse them in an appropriate way to extract the level information you need in fact popular tile based level editors such as tiled will even output the level descriptions as a lure script so you could call that directly to construct the required information you need and then you can parse it appropriately but for now i'm just sticking with a string and the string is 16 characters wide by 15 characters tall you'll notice the concatenation dots at the end of the string which allow me to graphically describe the level in text my c plus plus host program has no notion of what is the level at this moment so we'll create a function in c plus plus and call it with the dimensions of our map so when i prefix a function in lua with an underscore like this this means i'm going to be calling a function in my c plus host environment this is a javid x9 convention and you'll see i'm passing back to my host the host variable and the size width and height going back to c plus plus in the on user create function i'm calling the do file function which is executing the script in terms of execution our script currently doesn't do anything it simply defines a function called load level but it still needs to be executed with the do file function if for no other reason than to define what the load level function is so we can call it which is exactly what i want to do now so i'm going to get the global load level the name of our function in the lua script which if you recall from the previous video we'll place back on the lewis stack some callable index that represents that function we needed to pass two parameters to our load level lure function the first was this odd host parameter and what i'm going to do is call the lure push light user data which effectively means push a pointer i'm including the lower virtual machine and the pointer i'm passing is this as we'll see in a moment and maybe you can recall from the previous video that when lua calls a host c function those functions are defined statically it doesn't really know what object they belong to and we're calling these functions from an object that we've created in this case it's game world a subtype of the pixel game engine it could well be that we have many different objects each with their own unique lua virtual machines but there can only exist one host c plus function that it can call we can use this this variable or the host variable in the lua script to discriminate between the source objects that are calling the functions in the first place the second argument of our load level function was the level identifier itself and for this video there's only going to be the one level and it's going to exist at index 1. we'll call the load level function using the lower p call function and it falls well that'll return true successfully finishing on user create for us if we had syntax errors in our lua script they would have been caught by these check lure functions and displayed in the console our lua script is going to call this create level function as part of the hosts api so let's create that because i want this video to be somewhat informative i'm going to be doing quite simple things in an overly verbose way and i'm doing this because i hope it gives you some inspiration as to how you can represent things that are more complex using very similar techniques the level is going to be defined as array of tiles and for now i'm going to represent those as tile types empty or solid that's all i've got for this demonstration and i'm simply going to use a vector to store the tiles themselves in a variable called v level i'm also going to store the dimensions of the level as well somewhere in my game class i'm going to create a function called createlevel which takes in a width and a height and it sets our level size vector accordingly clears out anything that may be existing already and allocates enough space in our vector to store all of the elements of the tile map lure itself can't see this create level function directly nor can it call it either because this is a member function of a class and as you can see here contains nothing to do with lua at all i'm going to create a wrapper function instead now there are lots of third-party libraries which you can use which use various types of template blackmagic to do all of this for you but again i'm trying to be verbose and doing this by hand so i'm going to create a static wrapper function which is the one that lua will be able to see and i'm prefixing that with wrap now recall from the first video that these functions need to return an integer and they take in the lower state throughout this video i will be emitting lots and lots of error checking which you should really do when you're working with external scripts to make sure nobody is trying to hack your system but at a very basic level the first thing i'll do is make sure that line lure has called this function it has provided three arguments on the stack if we look at the script we're expecting it to return the host the size width and the size height on its own this error check is completely insufficient it doesn't notify the user of anything nor does it get the system into a known safe state so when you're doing this seriously you'll probably really want to think about how you handle errors this function is declared as static so it's the same function in any instances of this class therefore i can't simply call the createlevel function i've just defined because that create level function belongs to a particular class though i can now work out quite easily which class it belongs to because the first object provided on the lewis stack was the pointer to this object so this has been transferred to lua via the load level function and when we communicate back with the host in our createlevel function we send that pointer back therefore when i extract that pointer from the lure stack i can cast it to a gameworld object and it will be this gameworld object i'll also get the width and the height required and now i can call the create level function relative to this object with the width and height parameters all being well i can return 0 which means i'm not returning any information back to lure just to make sure that this relationship is working i'm going to run to cursor using the visual studio debugger to this line here which will only be called if our lua script has successfully moved the pointer around as necessary so i'll press ctrl and f10 here oh and it's just closed hmm something's gone wrong well let's see if any of our lure calls failed so instead i'm going to run the debugger to here so i can interrogate what the error messages might be and indeed there is an error message and if i put my cursor over it it says automation1.lua line27 attempt to call a nil value global underscore create level i.e it doesn't know what this function is and indeed it doesn't we've not told it so here i'm going to use the lua register function to register the name of our host function underscore create level and bind it to the wrap create level static function as part of our class so let's try this again i'm going to try and run the debugger to this line here this looks a bit more promising so this function has been called via our wrapper for the correct object and the arguments passed in are 16 and 15 very good that's now defined the vector that's going to hold all of the tiles in our level now this back and forth relationship between lure and c plus plus is just something we have to get used to but having the wrapper functions there gives us a chance to perform some degree of error checking and sanitization of the inputs lure is providing to see now as i mentioned before i'm doing a very simple thing but i'm doing it in a verbose way and so here i'm going to allow lua to physically construct the level in our game engine tile by tile by using two nested for loops to iterate through all of the cells in our string extract the character for a particular location and yes you'll have to deal with stuff like this in lua because lua counts from one and not zero i'm sure that was a good idea to somebody but not to most programmers but what we should see are some familiar one lone coda staples y times width plus x the offset of starting from one does upset that little bit you just have to deal with it but we can check that if the character is a period symbol then we're going to send through an empty tile and if it is a hash symbol we're going to send through a solid tile or a block in this case and we can also pass along the coordinates this requires the definition of a few more things firstly tile empty and tile block well i'm just going to define those at the top of my lua file as being 0 and 1. in the c plus file this ties into the enum class type secondly you'll see that we've got another host function here underscore set tile which means we need to go back to the c plus and provide this set tile implementation firstly i'll create a member function of the class set tile which takes in the position and an identifier for the tile this will be an identifier in an integer format from lua so i'm going to convert that to the necessary enum class type yes i could use casting to do the same thing but again i'm going back to this excuse or reasoning that keeping it simple and verbose may be more beneficial for you guys and given the coordinates that are returned from lua i index my vector appropriately and set the tile appropriately just as before i'm going to need a wrapper function which is the one lua actually calls i can take this opportunity to do some very basic error checking the first object returned is always going to be and this is just my protocol this is how i'm implementing these things it's going to be the pointer to this object then it's going to be the coordinates and the tile id itself and with that information i can call my set tile function just as an aside let's say some of this information was invalid well my set tile function could return as such saying it's invalid and i could then implement code here which pushes the results back on to the lewis stack so we could wrap this in some sort of error checking in lure itself we mustn't forget again to provide access to our wrapped function by registering it with the lua virtual machine and i will stand by my claim that last time this was omitted on purpose to extract the full educational benefit from debugging it i definitely didn't just forget but anyway now we've started to fill our vector of tiles with tile information we're going to need to draw these tiles to the screen i'll start by just clearing the whole screen to cyan and i'll create a little 2d integer vector to iterate through all of the tiles in the vector using two nested for loops by now the tile vector has been populated with solid or empty tiles and we also know what the size of the level is too i'll extract which particular tile we're drawing in this instance and then draw it if the tile is declared as empty i'm not going to do anything if it's declared as solid i'm going to use the pixel game engine's draw partial sprite function to draw a block sprite kindly provided by tutus on the discord server in the correct location so let's take a look and very nicely we can see a graphical representation of the level we defined in ascii in the externalized lua script file one of the benefits of scripting is we can easily make changes so if i add in three blocks up here i don't need to recompile anything i just run the program and we see our three blocks have appeared it can be argued that reducing compile times is one of the primary reasons you would want to go to an external scripting language as well as sort of modification and that sort of thing the projects i show on this channel are quite simple and thus compile very quickly but as your projects grow in sophistication and complexity the compile times will grow too and it gets quite frustrating if you just want to make a very simple change but have to wait 20 minutes at a time or even longer maybe for such a small change to be affected adding external scripting to your application is something that needs to be designed and it needs to be thought about from day zero of creating your application it's very difficult to shoehorn in external scripting later on so i would recommend that even if you don't know yet if you're going to need scripting but you think you might do start leaving some stubs and some functions hanging around or compartmentalize your application in such a way that it is easy to mix with a scripting language such as lua later on and this is no easy task and does require a bit of thought and some design decisions to be made one approach people often go with is to have a very small part of their application written in c plus plus and the rest of it written in lua for example you might just use the c for the rendering part and everything else including user input and the game loop exists in lua a natural extension of that might actually be to put the game loop in c plus so let's get some input here and we have that in a loop and for every frame of our rendering loop we call lure some sort of update function and with this update function we pass the equivalent of our f elapsed time or delta time between frames and we use lure to update every single object so the position of an object we do all of the kinematics and velocity stuff all of the collision detection and everything else happens in lua we just simply use c plus to do the input and the rendering i don't particularly endorse this way in some situations it is the right way but it relies a lot on luwa doing the hard work and lua although very flexible and really easy to use isn't the fastest language in the world it's no slouch but it's nowhere near as fast as just raw c-plus plus doing these things every frame we need to call lua and every frame we need lure to run its interpreter and it's just in time compiled stuff to perform all of the physics updates and game updates and everything else so really all of the critical crucial stuff is happening in the slower domain of the two we've got available to us there's also a considerable overhead in calling lua frequently let's say we lent on c plus plus a little bit more to handle all of our objects well nothing's really changed we still need to call an update function for all of the objects in the game and update their positions in fact potentially this situation could even be worse because we've got a lot more transactions between the c plus plus domain and the lower domain so we've got a lot more stack manipulations to worry about and a lot more error checking to perform the other extreme of course is to have everything in c plus plus and very rarely do any transactions with lure and this is what we did in the first video where we really just used lure as an elaborate configuration file once everything's been loaded in it's all left to the c plus plus to deal with for my application i'm going to aim for a nice happy medium the bulk of the hard work is going to happen in c plus so yes we're going to handle input we're going to handle object behaviors and physics and collisions and all that sort of thing and the rendering and i'm going to use lua to define our world but also and we're getting to the crux of this video to automate it automation could involve things like cut sequences or moving platforms how about object ai or simply game world interactions approaching things this way means all of the complex mathematics and the real cpu intensive stuff is being done by the c plus plus program and very occasionally we dip into the lua program to get some sort of update on how the c plus side of things should behave in my code it yourself role playing game series i did a similar thing but i did all of this world automation in c plus itself we used a reasonably trivial set of object-oriented programming principles to handle these interactions between objects it was a bit like scripting but using the c-plus program to facilitate that scripting therefore if we did make slight changes to the sequence of events that happened let's say a character walks to the top of the screen and turns left but we want to change it to turn right we still have to recompile the whole program but this did allow us to have quite elaborate scenes which at the time i claimed to be some sort of theater engine so just to reiterate all of the really tight loop stuff is being done in c-plus plus we'll generate a series of events at certain stages of our c-plus plus algorithm which will require lua to provide some sort of information in response to that event and what we'll see is that lua has one particular trick up its sleeve which as at this moment c plus doesn't provide though it i think it will in the future and that is the use of co-routines which are great for automating stuff so let's start building this up right now our world is fairly static it consists of a background being rendered of solid or not tiles things that move or things that are interactable within the game world i'm going to call dynamic objects and for the purposes of this video i'm going to simply define a dynamic object as a structure that has a position in the game world something that describes its size potentially a velocity an id which defines what it looks like and a flag which tells us whether the dynamic object should be removed from the world or not and i'm going to store all of the dynamic objects that are created in a vector of dynamic objects as well get this shared pointers not something we see very often on the one lone coda channel but we'll also see are not very easy to use with lua and we may see in part three of this series a slight change in this regard but for now this will work just fine it's going to be possible for the lua script to create a various number of dynamic objects so i'm going to create a create dynamic object function which takes in what is the type of the object and a position in space all this function does is create the dynamic object set its id in its position and pushes the object into the vector finally it returns the shared pointer naturally if we want lua to call this function we need to wrap it so we'll wrap it in a static function called wrap create dynamic object we see the pattern here we'll extract the parameters sent with this function the type and the position and this function is actually going to return back to lure the value of the pointer that's been created so in lua we will have some sort of unique identifier for that object i don't need to specify a index as such 0 1 2 3 4 i can just use the pointer itself and so unlike the other functions that we've created so far i need to push something back to the lower virtual machine in this case push light user data it's a pointer to the lewis state that was supplied with this function and here i'm calling the create dynamic object function specifically accessing the raw pointer underneath the shirt pointer and you can see where this might get a little bit messy later on but for this simple video it's going to suffice i need to return one to tell the lua virtual machine that this call is going to return one item of data in this case it's our pointer and as usual i will expose this create dynamic object function to the lua script in the pixel game engine's on user update function once we've drawn the level i'm then going to draw the dynamic objects on top of it which is just a case of iterating through the vector of dynamic objects and choosing an appropriate sprite for the type id that we've specified in the dynamic object structure going back to lua at the end of our level construction function i can call the create dynamic object function passing in the type and the position in the game world this function will return an identifier for that particular dynamic object and i'm going to store that in a player object variable so let's just take a quick look so here we can see the scene as before but now we've got a new object which looks a bit like marty mcfly from back to the future floating in space since this is the player object i might want the user to be able to control it and one of the advantages we have here is that a dynamic object is just a dynamic object potentially the player could control any dynamic object in the game so i'm going to create another function assign player control which takes in the unique identifier and will allow us to control that in the c plus program because the player can only control one object at a time i'm going to create another shared pointer specifically that points to the object that was deciding should be under player control and to our class it's time to allocate another pair of functions the first one simply assign player control which takes in a raw pointer to a dynamic object now this is where things get a little bit complicated because we've used smart pointers when the smart pointer went out to lure it went out as a raw pointer up here this dot get when lure is returning that pointer well there's no smart pointer wrapped around it so i want to find the corresponding smart pointer i have in my vector of dynamic objects which represents that specific area of memory this is a hack and can be improved but it is necessary to do it this way for now it may be very tempting to say we'll just wrap up this pointer in a new smart pointer but there'll only be one instance of that particular smart pointer and so when it goes out of scope it will delete the object it's pointing to even though existing smart pointers are already pointing to that data it goes without saying had i chosen not to use smart pointers in this instance life would be much simpler and it also makes an argument for perhaps not using the pointer value itself as a unique identifier but rather some sort of index however we'll see what happens with all of that in part 3. i'll need to wrap this function as usual in this case i'm getting the host object again and i'm getting the raw pointer value which is the only argument we're specifying as part of our automation script here assign player control the host and the player object i'll expose this function to our script but i also now need to implement the control for that object and this is very simple right now for this video we're not worrying about collision detection at all we're just going to move the object around the screen using the w s a and d keys by setting the velocity value of that dynamic object and updating its position accordingly modulated by f elapsed time this this is the sort of stuff we've done thousands of times now so let's take a look there's our object and as i hold down the keys i can move the object around in the scene but let's just quickly prove a point in my external script i've now created another player object and so without any recompilation of the c program i'm just going to run the script and we can see there are now two objects and the second one is under player control we've made a significant world and behavioral change without modifying the source program we're now starting to see the very starting stages of how lua is configuring a background game engine but this video is about automating things that are not under player control and instead will allow the c-plus plus environment to manipulate the objects under the guidance and supervision of the lua script i'm going to make a fundamental assumption at this point that any dynamic object in the game can be controlled automatically and this is one of those situations where we need to decide on a balance between what the game engine can provide and what lure is capable of implementing for us i'm going to implement a base structure called a manipulator as you can see it's abstract due to the presence of this pure virtual function the manipulator simply points to a particular dynamic object can update it somehow and signals when it has finished the manipulation i will derive from this base class various different types of manipulator that implement different types of motion in the game and we'll see it doesn't have to be restricted to motion either so let's assume i want one type of manipulator that linearly interpolates the object from a starting position to an ending position i'll call this move object and it will have some fields that represent the starting position and the target position i also want this motion to take a specific amount of time which will be specified and so i'll also want to keep track of how far into this motion have i got so far the constructor for this manipulator takes in a pointer to the object the target location and the time it needs to pass to get there and we'll see that the starting position of this manipulation is the object's current position the update function will be called by c plus every frame the first thing i'll do is accumulate how much time has passed f elapsed time is the time between two successive frames and i will explicitly set the dynamics object's position by taking the difference of the target and the start position multiplying that by normalized time so in this case this will yield a value between 0 and 1 and offset it by the starting position this is a two-dimensional linear interpolation nothing more and i talked about this in my essential mathematics for game dev video if the time that's passed so far is greater than or equal to the time we expected the motion to take then i'm going to clamp the dynamic object's position at the end location the target position and set the flag completed to true by default this flag is false my update function will return the value of this flag i'll also add to my class two more vectors both have shared pointers to type manipulator this one the manipulators contains the manipulators currently in effect this frame curiously i'm also creating a vector called v new manipulators and we'll see why in a moment in on user update before we do any player control i'm going to update all of my active manipulators and i'll do that with a little auto for loop using polymorphism i'll call the update function and if that returns true that means this manipulation has completed and it's at this point that we now need to dip into lure and start looking at how we structure things there in my lua script i'm going to define an empty table called dynamics and for now i'm going to leave the player object 1 in but i'm going to remove player object 2. i want to add a moving platform to the game so i'll call the create dynamic object and specify the type which in this case two is moving platform and the starting position but i want this particular moving platform to follow a certain path and this is where we're going to start using lua co-routines this function dynamic behavior 1 is going to be unique for this platform and specify the path the platform should take i never want it to stop so the first thing i'll do is create a while loop so the platform is basically going to move around four different locations around the screen to move it to a location we'll need to create a function called move object in our c plus plus program and call it from here and i want it to move from that location to that location to this third location and this fourth location these are the coordinates and this is the time i want it to take so it'll take five seconds to move each time but at the moment you may see a bit of a problem here if anything calls this function it never ends it's a wild true loop it will just continuously keep calling these functions in our host program this is not a good thing co-routines allow us to do something a little bit strange to us c plus developers and that is we can abort the function at a particular point but we can come back to it at that point later on so i want to sit in this loop and i want it to start moving the platform to that location the c plus plus program is going to go away and move it but i don't want to start issuing more moves in the background i want to wait until the c plus plus program signals well that object has arrived at that location and a co-routine will allow me to do just that i can yield which means it'll execute this line it'll execute this line and then it'll stop here until it is signaled to continue naturally i want to do this for all of my movements now the cool thing about a co-routine is even though this is yielded i can still call and run other parts of the lua code in many ways you can think of this as a thread though i will emphasize it isn't a thread now i've created a unique function that represents the behavior of that dynamic object i want to associate that behavior with the moving block dynamic object we created earlier and this is one of the most beautiful parts of lua in my dynamics table i can index it with the unique identifier of our moving block and specify a particular property called behavior to represent what is effectively a function pointer to a co routine that's been created based on our dynamic behavior 1 function and i'll demonstrate later this is now trivial to associate different behaviors with different objects or the same object or change the behavior of an object when you call co-routine create it doesn't actually run the function straight away all this has done is created the co routine we need to start the co-routine and to do that i'm going to create a lua function called issue next task note the lack of an underscore at the start this is going to be something we implement in lua and i'm passing to this function are pointed to our host environment as always but also the pointer to the dynamic object so let's create this issue next task function here the first thing i'll check for is if the co routine is dead or not a co routine that's dead is one that has managed to exit to the function in this case our dynamic behaviour one function can't ever exit so the coroutine's status is never going to be dead but that doesn't mean we can't have something which is only executed once for example and this is the important line of code coroutine dot resume and we pass in our function pointer to the behavior that we've associated currently with that dynamic object and we also pass in the two arguments to the function so let's just compile this in our minds for the time being we're creating a dynamic object and we're telling it to follow the behavior defined by co-routine dynamic behavior 1 and then we call the issue next task function the issue next task function makes sure that the co routine actually exists and then it calls the coroutine.resume function now the first time you call that on a co routine it effectively starts the function so it'll hit this while true do which will then call this move object in our c plus environment and then it will yield at this point lua has finished it will stop there once the movement has finished in our c-plus plus program i.e the manipulator has finished moving the dynamic object we can call this issue next task function again the co routine gets resumed but because the co routine is already running it resumes from the point that it was last yielded so we run this line and then wait again and we'll keep doing this in a loop so now we need our manipulator in c plus plus to call issue next task once that manipulation has completed this is simply a case of finding the function and calling it and all we're going to push to the function is that this object again as we did before and we're also pushing the pointer to the dynamic object it's important to push that pointer because that pointer is used as an index into our dynamics table so we can get the correct co-routine i.e we're going to reuse this function for all of the objects in our game this is really convenient and this is one of the really nice things about lua being as flexible as it is because everything is stored as key value pairs you can do lots of interesting tricks such as storing functions as properties of other objects so in c plus when we're updating our manipulators for most of the time they're doing the hard work positioning the object in the right location and lua isn't doing anything at all it's just sitting there waiting now for a call to issue next task which may only happen once every 100 or 1000 frames or so it's not a very common occurrence so we've kept our lure overhead down and made sure that c plus plus is doing the hard bit the policy we've decided upon is that our manipulators do a simple amount of movement at a time so when that manipulation is complete i want to remove that manipulator from our vector of manipulators which i'm doing using the erase if function of the standard library which just checks to see if the complete flag has been set now here's an interesting thing when we call the issue next task in our lua script that we'll call potentially move object or various things which will generate new manipulators we can't push those new manipulators directly into this vector of manipulators because we're iterating through this loop and we don't want to change the container we're currently iterating through that can lead to some really nasty things happening so any new manipulators that are generated as a result of calling issue next task we will place into our vector of new manipulators and each frame i'm going to copy that vector over into our active manipulator vector and indeed clear it so now we need to implement this move object function and as before it's a pair of functions the first one is move object which takes the object the location to move it to and the amount of time we want it to take and we can place that at the back of our new manipulators vector and you'll see here it's all in one line i'm creating a shared pointer to the manipulation object in this case it's a type manip move object and we pass in the relevant data and i'll want to wrap that as well usual stuff get the appropriate pointers get the information we want to pass to the function and ultimately call our function and finally expose the move object function to our lua script so let's take a look and then analyze to see if it's working properly so we can see a dynamic object has been created it looks like a moving platform and the moving platform is following a path automatically around our scene we assigned player control to the player so i can move the player around and in the background our moving platform is quite happily doing its own thing so it seems on the surface everything is working just fine but i just want to prove that lua isn't indeed always computing things and to demonstrate this in my loop here i'm going to use lua's print function start of circular motion and in the issue next task function i'm going to print next task issued now the nice thing about lua and the console and indeed the pixel game engine having a console in the background is we can actually see these outputs from lua whilst our program is running so notice again no compile time that's the beauty of external scripting and we can see that next task issued is called very infrequently only when the manipulation has finished and either platform has gotten to that target location and once it's finished its loop of four next task issued start of circular motion so here lua is controlling for us quite a complicated sequence of events but it's barely being invoked by the host application this is a great scenario because it means c plus is doing the hard work and lua isn't and as you start developing applications such as this you'll find yourself operating more and more in the scripting language domain rather than the c plus language domain so let's add another moving platform without touching any of the c plus plus at all this time i'm going to add moving platform 2 give it a start location and i'm going to create a new dynamic behavior function specifically for this platform this is just going to be a platform that moves up and down let's take a look so now we've got two moving platforms one just moves up and down one's moving around in a circular motion again we can see lua isn't doing very much at all in the background we didn't change our c plus program but we've changed the element and nature of the game and of course these automations can be tweaked and customized as necessary so this command was moving the platform up in four seconds maybe i want it to fall back down in one second without any re-compilation it goes up and it falls back down very rapidly there's no collision detection yet that's coming in part three which we'll see we'll also use to implement various triggers to cause other things to happen but let's add a slightly more complicated motion in fact i'm going to add two more the first one is actually very simple teleport object it doesn't move anything at all it just immediately positions a dynamic object at a set location so this specific manipulator doesn't live for a very long time this next manipulator is going to move the objects given a curved path and it's just going to be a simple bezier spline so you specify a starting location which is where the object currently is some sort of waypoint it doesn't go through it but it biases it towards it and a target location and again as before a time too you'll actually see it's very similar to how we did things before except this time i'm using the spline equation instead and i have a video just about splines it doesn't cover bezier splines but it'll give you an idea about how this works it's effectively a non-linear interpolation but other than that it's exactly the same as our linear interpolated movement naturally these new manipulators are going to require new functions so here are the functions for the teleport object and here are the functions for the move object with curvature you may decide in your interface not to have lots of individual functions but to have one which is customized accordingly in lua and i thought it would be nice to just finish the video off with a simple example of how you can customize a behavior which might be used as part of a tutorial or something like that and to create a behavior called run and jump and since i'm just demonstrating it i'm going to keep this in a while loop as well don't forget these don't necessarily have to be kept in while loops they can be just fire and forget behaviors once the co-routine has died there's nothing else for it to do the dynamic object will just exist in that location until it's instructed to do something else either by the host application or by some other function in the script but just keeping them repeated makes it easy to debug them the first thing i'll do is teleport the object to a specific location and then i'll yield i'm then going to do three movements one moves the object linearly then we're going to move the object with a curve and then we're going to move the object linearly again down where we're creating dynamic objects i'll create one called player buddy and associate it with this run and jump behavior let's take a look so now we can see the other player object or potential player object is running jumping and landing it's quite an elaborate cut sequence and in the background our platforms are still moving and we still have player control once you've established a set of basic manipulations for the dynamic objects you could create quite elaborate sequences you could also have the sequence behavior functions operate based on conditional information and we'll look at all of that in part three and so that's that it's early days yet in this project but i'm hoping you're beginning to see the benefits of using a language like lua as an external scripting language for your projects if you've enjoyed this video please give me a big thumbs up have a think about subscribing come and have a chat on the discord server all of the source code for this video will be uploaded to the github and i'll see you next time take care
Info
Channel: javidx9
Views: 38,071
Rating: 4.9814568 out of 5
Keywords: one lone coder, onelonecoder, learning, programming, tutorial, c++, beginner, olcconsolegameengine, command prompt, ascii, game, game engine, pixelgameengine, olc::pixelgameengine
Id: E42Lyv2Ra1c
Channel Id: undefined
Length: 45min 23sec (2723 seconds)
Published: Sat Aug 15 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.