Lua for GrandMA3 Episode 8 - Conditional Loops
Hey lighting folks! The following is the transcript from my YouTube video, Lua for MA3 Ep 8 - Conditional Loops
This video is part of my Lua for GrandMA3 Tutorial series. Please check out my channel, From Dark To Light, on YouTube, and you can find the code to go along with my tutorials here on GitHub.
Hello lighting people, welcome back to my Lua for GrandMA3 tutorial series! Today we're going to continue talking about loops, but of a different kind than we've discussed so far. Unlike for loops, which are basically going through various iterations of things and repeating until all the iterations are completed or the loop is broken, conditional loops are more like if statements; that is they only repeat if the specified condition is met – but unlike if statements, they repeat until the condition is no longer met. There are two types of conditional loops that work only slightly differently, so let's talk about them.
The two types of conditional loops we're going to talk about are “While-Do” and “Repeat-Until,” or, as they're properly called, just “While” and “Repeat,” but to be honest I find it easier to remember how they work by thinking of them the first way I mentioned them.
Now, you already know this, but just to review, the way a conditional statement works is, “if this is true then do that, else if this other is true then do that other, end;” but sometimes you need to do things multiple times.
I'll just create an example of trying to do this with “if” to show you how frustrating it can be. I'm going to be incrementing MyNum and doing an action at every number, like this. I've already set MyNum equal to zero; now I'm going to type “if (MyNum < 10) then Printf(‘My number is less than 10’) else Printf(‘My number is 10 now!’)” and this already auto-completed “end” for me, which is really nice.
In case you missed the last video I made, I recently started using an extension that works with MA3 API and Visual Studio Code. You can find it under the extensions in VS Code and it is absolutely amazing. I only just now found out that it also auto-completes if statements; that's really cool. There are so many cool things it does and I haven't even figured out what all they are yet, but to continue this now, I'm going to go on and do “MyNum = MyNum + 1” and then “if (MyNum < 10) then Printf(‘My number is less than 10’) else Printf(‘My number is 10 now!’)” and, okay, you get the point. That does not make any sense to continue doing this. Of course, one way to do this instead would be to place the original if statement and addition; that is to say, this if statement and this addition, inside of a function called CheckNum with the argument MyNum and run it 10 times.
Actually though the problem with that solution as well as this one is that they're both very limiting. They for sure only work if you know exactly how many times you need to compare the variable, and if you already know how many times to do the action, why bother comparing it now? I'm sure you're thinking, “why don't you just use a for loop?” and you wouldn't be wrong, if all I needed was these numbers, however, depending on where my numbers are coming from I may not be able to use a for loop in every situation; for example, if I'm just getting a random number and I know that number is changing and every time it changes I need to do this comparison but I don't know what it's changing from or to, then I would definitely need some way to do an action based on what that number is that's more efficient than this, because honestly, I don't know how I would make this work in this situation.
And I know this likely doesn't make much sense now, but don't worry, because I'll wrap up with a real-world example before the end of the video. For now, let's focus on the concept.
If only there was some way to keep looping this if statement until MyNum reaches 10 and then stop it... Wait! There is! Allow me to introduce the magic of a while loop. I'm going to take away these if statements because honestly I don't think you're going to need them, and so I've set MyNum equal to zero, and now I'm going to type “while (MyNum < 10) do Printf(‘MyNum is less than 10’)” and “MyNum = MyNum + 1” then after the “end” let's do “Printf(‘My number is 10 now!’)”
Alright, let me explain. So the loop checks the condition. MyNum is less than 10; after all, we just set it to zero, so MyNum is less than 10; the while loop executes the contents of the statement – that would be this right here, and then checks the while loop again rather than moving on at the end. It repeats, so you go back through here; again, MyNum is still less than 10, so we execute the contents, repeat again, and so on. As soon as the condition is no longer valid it immediately breaks out of the loop and now whatever comes after it gets executed. So once this gets to 10 it goes “is MyNum less than 10? Nope, it's not, okay. Go to to the end and execute whatever comes after it.” So then it will print “My number is 10 now!” which would be accurate at that point.
As with a for loop you can always use break to get out of a loop early.
Now this is a nice option to have, but while loops always check the condition first and then execute only if it's true. What if you need a loop to run once before checking and then check the solution to see if it needs to repeat or not? There's another option for that. The repeat loop runs the contents of the loop, checks the condition, and if the condition is false it repeats until the condition is true. The repeat loop is written like this. I've already set MyNum equal to zero. I'm just going to go down here and basically use that same variable. We're going to assume that this code doesn't exist right here, and I'm going to type “repeat.” This extension so nicely auto-completed the repeat for me, but I need to place some info inside it, so I'm going to do that. Going to “repeat MyNum = MyNum + 1 if (MyNum == 2 then Printf(‘My number is 2’) else Printf(‘My number is not 2.’)” and “end” and then after this “until” I'm going to put the condition, which is going to say “MyNum > 2”
I'm going to go ahead and actually copy this “MyNum” setting right here. I don't mind using the same variable, but I'm going to have to reset it in order to reuse it so I can show both of these examples. And now let's run it.
And over here in MA3 I do have these right here that I talked about in the last video, as I mentioned already. I'm not going to use the A_Art plugin today because I have to have my USB drive in order to load a new plugin with it and I left it at church today, so I am just going to copy and paste code like I've done before, but I do intend to use the A_Art plugin in future videos because it is going to be more convenient than copying and pasting.
So for now I'll just go ahead and do this and run it, and as we can see we have 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 instances of the sentence “My num is less than 10” and then it says “My num is 10 now!” that would be because MyNum went from 0 to 10 and then it was 10. And then it says “My number is not 2, my number is 2, my number is not 2;” just to review, that would be because right here we have “MyNum is 0,” then we add 1 to it, that makes it 1. 1 is not equal to 2 so my number is not 2. MyNum is not greater than 2 so we're going to repeat this. MyNum + 1 is 2, so MyNum is 2. We print that. MyNum is not greater than 2 so we repeat again; 1 more is added, it's no longer equal to 2, so my number is not 2, and now MyNum is greater than 2, so the repeat loop ends.
Now I'm going to go ahead and show a real world example of this. I am going to paste some code I already have prepped and it looks like this. So I'm just going to go over this and show you basically how it works; I'm not going to run it because by itself it doesn't actually do anything, but as always you're going to have access to this code on GitHub, so you can play with it, tweak it, alter it, and do whatever you need to do to see how it works and see how you can make it work in even cooler ways! This plugin is designed similarly to a small piece of the same plugin I pulled an example from for the last Lua tutorial video.
My plugin, among other things, actually finds out from the user what songs they need sequences for and copies the sequences from a database to a location where they can be used and modified without impacting the master sequence, but sometimes the location that I'm trying to copy them to has sequences in already, so here's how I handle that.
I am going to be reusing the MA3 functions I showed a while back that I haven't fully explained yet to check if a sequence exists. Don't worry, we'll get to that soon.
This loop serves the purpose of finding out if the song's location is occupied and, if so, whether the user would like to use the existing sequences rather than copy new ones, or delete the existing ones so that they can copy new ones, or try a different location, or just cancel the action. So first we check the variable “SongSetup” to see if it is nil, and it is, meaning we have not yet resolved this question.
Now, is the location for the songs occupied? That is what this piece does right here. This function returns a boolean and so, if this is false then that means that there is no object in SequenceLocation, in this case 1, so all of this is skipped because this is false, and we go to the else, which sets SongSetup equal to “Approved.” In this case then, the while loop ends, because with SongSetup not being equal to nil it cannot repeat, and SongSetup being equal to “Approved” means that everything can go on as it should with the rest of the plugin.
Now suppose there is an object here. This returns “true,” then what is inside of here gets executed and in this case that puts a message box on the screen saying “The location for your sequences is currently occupied; would you like to use the existing ones, delete them, try a different location, or cancel the setup?” and if the user answers with option 1 then sequence 1 thru 4 is deleted, SongSetup is set to “Approved,” the rest of this doesn't happen, and the rest of the plugin continues as it should because SongSetup is equal to “Approved.”
If the user chooses to use them then SongSetup is equal to “Assignments Only” and that means that instead of asking the user what songs to copy and then copying new sequences and doing a whole bunch of extra things it only assigns the existing sequences to executors so that they can be used. If the user says “Try a different location” then that's where the while loop really comes into play, because the sequence location is changed to whatever it currently was plus 10, and SongSetup is not changed, meaning it is still equal to nil and this loop repeats, and of course the same cycle happens again.
Now, it was intentional for me to use an “else” right here, so that if either the user presses “Cancel Setup” or something else causes something to happen, like maybe the user pressed escape and didn't choose an option, it's going to set SongSetup to “Canceled” so nothing crazy will happen.
In this use case the loop is sort of more of an if statement that can be easily repeated if necessary but it rarely actually is. It is really important to have that option though! Let me show you why. If the user chooses to try a different location, we add 10 to the sequence location and try that, but then that one also has something in it, then we have to have the option to try again, right? And so then we have, within this, we have to put this whole if statement again, and then within that we have to put this whole if statement again, and again, and again, and where does it end? Then what you have is a plugin that only allows you to try a certain number of locations, and that's kind of limiting, and obviously my show file is limiting in itself because I have other things in my sequence pool and I wouldn't want to put my songs just anywhere (and you could also put a cap on this, of course), but you don't want your plugin to be limiting you; that's why this while loop is helpful, because every single time – you can run this a million times, it's not going to matter. It's going to just keep looping the entire time, and it's the same amount of code. You don't have to write out more code and have more potential for errors and all the stuff that comes with that.
Now in this case I used a While loop, but you can totally use a Repeat loop as well; that would just look like “repeat” and then instead of “end” we'll type “until SongSetup ~= nil” and that would do basically the same thing.
It does not check for anything at the top, it just runs it, but we for sure want it to run once anyway, right? So it would go right through it and then if the object was not valid or the user had chosen an option that allowed the song setup variable to be set then it would not run again, because SongSetup would already be not equal to nil, but if the song setup was still equal to nil by the end of it then it would repeat, and so on, and so it would basically work the same way as a While loop.
In most cases you can pretty much use either one, but a lot of the time one is more intuitive than the other, and in some cases it really is important to use one over the other; so it's just really important to understand both and how either one could be used to your advantage in different scenarios.
Alright, it is finally time, you guys! I've been teasing for a while about the option to locate sequences by name or location and find out if they exist. That information is going to be in the next video! Besides seeing if a sequence exists, I'm going to show you several different ways to search for sequences using different data depending on what you have available, identify a sequence or cue by its handle and read or write pretty much any type of data to that sequence or cue using Lua. I'm really excited to share this with you because it will absolutely change the way you see MA3 and understand the way it works. I'll see you then but for now I hope you have a fantastic day and happy programming!
Comments
Post a Comment