You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
82 lines
5.5 KiB
Plaintext
82 lines
5.5 KiB
Plaintext
<!--201001,201112-->
|
|
<h1>coroutines in godot engine </h1>
|
|
september 17, 2020<br>
|
|
#programming<br>
|
|
<br>
|
|
<b>Coroutines</b> are functions that, instead of running to completion, can yield until certain criteria are met. Godot Engine supports coroutines through <a href="https://docs.godotengine.org/en/stable/classes/class_@gdscript.html#class-gdscript-method-yield"><b>yield</b> ( Object object=null, String signal="")</a>, <a href="https://docs.godotengine.org/en/stable/classes/class_gdscriptfunctionstate.html#class-gdscriptfunctionstate-method-resume"><b>resume</b></a>, and the <a href="https://docs.godotengine.org/en/stable/classes/class_gdscriptfunctionstate.html"><b>GDScriptFunctionState</b></a> object.<br>
|
|
<br>
|
|
<h2>why use a coroutine? </h2>
|
|
<br>
|
|
Coroutines allow for scripted game scenarios that respond dynamically to the player and the changing game world. They let you bounce between functions, step-by-step, and respond to interruptions. This means functions can be automatically called at the completion of other functions, animations, player actions, in-game events, or timers. Add in interruptions and conditionals, and you have a tool for building a responsive game world. <br>
|
|
<br>
|
|
<br>
|
|
<h2>stoplight example </h2>
|
|
<br>
|
|
As a basic example of coroutines in Godot Engine, I made a stoplight. Follow along with my code on <a href="https://gitlab.com/chimchooree/stoplight">GitLab</a>. <br>
|
|
<br>
|
|
In my example, the light changes every few seconds, going from green, yellow, then finally red. The light changes immediately if the Walk Button is pressed. This project demonstrates methods that can wait, resume, and be affected through player action. <br>
|
|
<br>
|
|
<center>
|
|
<a target="_blank" href="/static/img/ent/stoplight_demonstration.gif">
|
|
<img src="/static/img/ent/stoplight_demonstration.gif" alt="(gif: demonstration)" width="500" height="281.67">
|
|
</a><br>
|
|
</center>
|
|
<br>
|
|
<br>
|
|
<h2>how does it work? </h2>
|
|
<br>
|
|
<h3>node hierarchy </h3>
|
|
<br><center>
|
|
<img src="/static/img/ent/stoplight_nodehierarchy.png" alt="(image: node hierarchy - Root is a node named Main. It's children are TextureRect BG, AnimatedSprite Stoplight, Sprite WalkButton, and a Label. Stoplight's child is a Sprite. WalkButton's child is a TextureButton.)"><br>
|
|
</center>
|
|
<br>
|
|
<br>
|
|
I have a TextureRect background, an AnimatedSprite stoplight, a Sprite walk button with a TextureButton, and a label for displaying a timer. Since this is a simple example, most of the code is attached to the root. It's better to have code closer to where it's being used and to watch your separation of concerns in real projects, though. <br>
|
|
<br>
|
|
<br>
|
|
<h3>animation</h3>
|
|
<br>
|
|
<center>
|
|
<img src="/static/img/ent/stoplight_animationframes.png" alt="(image: the AnimatedSprite Stoplight has 4 animations - default (which is no light), green, red, and yellow.)"><br>
|
|
</center><br>
|
|
The light is changed by setting its animation to one of these options. Each is one-frame - just the stoplight with the one or none of the lights colored in. <br>
|
|
<br>
|
|
<br>
|
|
<h3>the code </h3>
|
|
<br>
|
|
This project has two scripts: Main.gd, which is attached to the root node, and Label.gd, which is attached to the Label. <br>
|
|
<br>
|
|
<b>Main.gd</b> - code available on <a href="https://gitlab.com/chimchooree/stoplight/-/blob/master/Main.gd">GitLab</a><br>
|
|
<center>
|
|
<img src="/static/img/ent/stoplight_main.png" alt="(image: Main script.)"><br>
|
|
</center>
|
|
<br>
|
|
<b>Label.gd</b> - code available on <a href="https://gitlab.com/chimchooree/stoplight/-/blob/master/Label.gd">GitLab</a><br>
|
|
<center>
|
|
<img src="/static/img/ent/stoplight_label.png" alt="(image: Label script.)"><br>
|
|
</center>
|
|
<br>
|
|
<br>
|
|
<h3>how the code works </h3>
|
|
<br>
|
|
At <code>_ready()</code>, <code>wait()</code> is assigned to the GDScriptFunctionState <code>result</code> and is called for the first color, green. <code>_ready()</code> yields until the given function <code>wait()</code> is completed. <br>
|
|
<br>
|
|
The wait method yields for the given amount of seconds then sets the stoplight to the given color. <br>
|
|
<br>
|
|
At <code>wait()</code>'s completion, <code>_ready()</code> calls <code>wait()</code> for yellow, then red. Each is called one at a time, waiting for the color to complete before moving on. <br>
|
|
<br>
|
|
<br>
|
|
<h3>interrupting the stoplight </h3>
|
|
<br>
|
|
The Wait Button interrupts the wait times between colors. Before <code>_ready()</code> yields, it connects the <code>'pressed'</code> signal on the Wait Button. <br>
|
|
<br>
|
|
If the Wait Button is clicked during <code>wait()</code>'s yield, the GDScriptFunctionState <code>result</code> resumes immediately, ignoring <code>wait()</code>'s yield timer. This time, <code>result</code> has a string arg <code>'interrupted on green'</code>, so it will print the result, change the stoplight's color, then print <code>'done: green'</code>. The <code>wait</code> method is complete, so <code>_ready()</code> resumes and calls <code>wait()</code> for the next color. <br>
|
|
<br>
|
|
<br>
|
|
<h2>applications </h2>
|
|
<br>
|
|
The outcomes in this example be swapped out for anything. I use coroutines in blessfrey's skills to manage the flow of phases from activation, different phases of effects, cooldown, and interactions with any counters. I also use it in the basic weapon attack so the character continuously swings at the rate of his attack speed until he cancels, uses a skill, or moves. It could also be used for something like cars that stop and honk when the player walks in front of them and drive off once the path is clear. <br>
|
|
<br>
|
|
Coroutines enable lots of practical ways to improve the flow and interactivity of your game, so just keep experimenting. <br>
|
|
<br>
|