Jump to content

Lua Coroutines Tutorial


Josh
 Share

Recommended Posts

I am looking to hire someone to create a tutorial that explains Coroutines and provides a simple example showing how they can be used. Any artwork you need will be provided if you give me a description of exactly what models you need (if any). Please post here if interested.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

This will be a written tutorial to be added to the lessons here:

https://www.leadwerks.com/learn?page=Tutorials_Lua-Scripting

 

A simple example that shows how coroutines can be used would be helpful for many people. The key is to make it simple and understandable to illustrate the idea.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

I've used them before in my cutscenes so I know how they work. I'd be willing to simplify that idea and make a written tutorial and basic working LE example to show the idea. If you pick me to do this I wouldn't want money but I would like an XL LE hoodie :)

  • Upvote 2
Link to comment
Share on other sites

I'm formulating an idea still. Things that work well are playing audio files and waiting until they are finished to continue to something else. Wait statements that continue after x seconds. Moving things and continuing after it's reached it's destination. Possibly wait on user input to continue.

 

Something like

fits coroutines well. Switching images with audio playing maybe with some wait statements in there. If you notice a lot of images switch when audio is finished. Something similar is Max Payne cut-scenes:

 

These would normally require a lot of state type variables but with coroutines the entire thing becomes sequential in 1 function. Easy to understand and create. So maybe an intro like these?

Link to comment
Share on other sites

I could create an LE feature cut scene using the images and text on https://www.leadwerks.com/engine. I wouldn't need anything extra from you then as I can just download those images from the site. Maybe find some epic background music thst you'd like to play while that's going on?

 

Give me the weekend and I'll have something to show. Would you like me to just upload the project to this post when I have something or privately to you?

Link to comment
Share on other sites

Okay, so what you actually need are a few storyboard illustrations, some voice tracks, and a background song.

 

How many and what resolution images? Approximately how long do you want each image to be displayed for? Any other specs?

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Okay, so what you actually need are a few storyboard illustrations, some voice tracks, and a background song.

 

How many and what resolution images? Approximately how long do you want each image to be displayed for? Any other specs?

 

If you want to do a pretty looking intro then yes. I'll prove out the simple example I listed above tonight which will take a couple mins since I have most of the code already. As for wanting to do more of a real game type of intro:

 

Maybe 3 different images. As for resolution I'll have the screen res at 1280x720 so how about these images be 1920x1080? Thst way I can move them around sort of like how Orcs Must Die does. The length of time they are displays is part of what the coroutine will do so that doesn't have any art requirement.

 

As far as what the images themselves have on them I'm not picky. If you have ideas on what you'd think would be cool I'm open. They should have points of interest on them and be showing some kind of battle happening maybe? Maybe action shots of orcs vs humans war focusing on a special human king and orc king? I have no idea if I'm asking for a lot by asking for those kinds of images. Not sure where you get them from.

Link to comment
Share on other sites

Do you mind if I use this simple one file tween library I have? It makes changing values from a to b, over a given amount of time, with different easings, simple. It's simple to use so shouldn't distract from the idea of coroutines. This would make it easy to zoom and move the images like in the orcs must die intro which is a cool effect.

Link to comment
Share on other sites

Sounds fine. Can you also do a cross-fade transition? That seems like it would be easy with coroutines.

 

It basically works like this, right?

 

function Update()
   while alpha<1.0 do
       alpha = alpha + 0.05
       SetColor(1,1,1,alpha)
       DrawImage()
       yield
   end
end

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Was messing around a little this evening. Here's a quick typewriter effect script. The biggest disadvantage to coroutine functions is that if there is an error we get no error message popup. The function just stops working. So you have to put a lot of print statements in usually to see where it's erroring out and I usually can just see why. I'm sure LE could capture the errors as I'm sure the language allows for it but might have to do something special on the interpreter side.

 

function Script:Start()
self.co = coroutine.create(Intro)
self.text = ""
end

function Script:UpdateWorld()
if coroutine.status(self.co) ~= "dead" then
coroutine.resume(self.co, self)
end
end

function Script:PostRender(context)
context:SetBlendMode(Blend.Alpha)
context:DrawText(self.text, 0, 0)
context:SetBlendMode(Blend.Solid)
end

-- helper function to wait x ms
function Wait(ms)
local time = Time:GetCurrent()
while Time:GetCurrent() < time + ms do
coroutine.yield()
end
end

-- helper function to give a typewriter effect where each letter in a string is printed at a give interval
function Typewriter(script, fullText, delay)
for i = 1, #fullText do
local c = fullText:sub(i,i)
script.text = script.text..c
Wait(delay)
end
end

-- below is the function that we run in the coroutine
function Intro(script)
script.text = "I'm about to do a typewriter effect. Hold on!"

Wait(5000)

script.text = ""

Typewriter(script, "This is a test to see if this work! And it does!", 150)
end

  • Upvote 2
Link to comment
Share on other sites

Oh yeah, it worked and it looks cool.  But the point of this lesson is to teach a concept, not provide a new feature.  That example has a lot of code and we only need to illustrate the basic idea.

Can you do a simple object script that types text on the center of the screen, with a pause between each letter, and then make it fade out?  It should have an optional Activate() function and a Boolean property called "active" that defaults to true.  Then all the user has to do is attach it to an object in a map, enter their text, run the map, and they can see it in action.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Typewriter script example to show a usage of coroutines. Now of course you could do this without coroutines (you can do everything without coroutines), but coroutines tend to make longer, more sequence like code easier to write, read, and maintain given it manages state for you which means less state variables required to manage and less jumping around in code during the sequence to understand where you are in the sequence.

https://drive.google.com/open?id=0B0eHAaVNOFUwQnA4MzY4UTEzUnM

 

So as you can see it's up to the user to manage the coroutine. What would be pretty cool is to somehow mark a user defined script function as a coroutine function so we can just use coroutine.yield() inside of it automatically. When the function is then called by whatever, the engine would then need to wrap it in a coroutine, and store it in a list and then each frame resume into the coroutines that entity currently has running. This would make things more natural. For example if I had a character script with a custom function of Script:DoSequenceOfEvents(), inside that function all the sequence of events could be done, vs now we basically have to set a state and then manage that inside UpdateWorld(). This means UpdateWorld() needs to exist and always be called every frame even if we aren't doing DoSequenceOfEvents(), which most of the time we aren't. This would allow having a lot more scripts in a scene that can do things.

 

When we were working on urWorld we had scripts attached to trees and then had a ton of trees, but we noticed we had to remove UpdateWorld() function from the scripts otherwise performance was horrible. This meant, of course, the trees had no meaningful functionality that could be done because today you need UpdateWorld() to do stuff. With coroutines this wouldn't be the case. Functions that actually do things over time could be created and they aren't ran until called and when finished they are dead and not called anymore. Coroutines give us sort of a dynamic UpdateWorld() that's only called when we need it instead of every frame which is a huge performance boost when you have a lot of stuff in your scene with scripts. Of course if you have to do something every frame then you still need UpdateWorld() but there are cases where you just need to do things over time on that object when called.

Link to comment
Share on other sites

This is a very good example of coroutines!  The script looks good, with a few changes:

  • If no font is specified, no font should be loaded.
  • The loaded font isn't actually being used.  When you use it in PostRender (if one is loaded) you need to get the current font, increment the ref count to prevent it from being deleted, set your script's font, then reset the current font and Release it, to maintain the same ref count.  It is also possible GetFont() could return nil, so account for that possibility as well.
  • Script should be stored in "Scripts/Objects/UI"
  • Sound should be stored in "Scripts/Objects/UI/typewriter.wav"
  • If the sound is not loaded, skip playing it: if self.sound~=nil then self,sound:Play() end
  • Calculating the center of the screen should be done with the current context, not the current window.
  • A customizable delay should be added after the text is finished and before it starts fading.

If you can write a tutorial on this, a hoodie and a beer will be yours in a couple weeks.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...