Implementation of cutscenes and sequences of actions in games
3r3992. 3r3-31. 3r3-3960. In this post I will talk about how you can implement action sequences and cutscenes in video games. This article is a translation of Here is this article and on the same topic I gave a talk on Lua in Moscow, so if you like watching a video more, you can watch here is . 3r3809. 3r3992. 3r3809. 3r3992. The article code is written in Lua, but can easily be written in other languages (with the exception of the method that uses korutiny, since they are not in all languages). 3r3809. 3r3992. 3r33961. 3r3809. 3r3992. The article shows how to create a mechanism that allows you to write the following cutscenes:
3r3992. 3r3809. 3r3992. 3r3907. 3r3908. local function cutscene (player, npc)
player: goTo (npc)
if player: hasCompleted (quest) then
npc: say ("You did it!")
delay (0.5) 3r3992. npc: say ("Thank you")
else
npc: say ("Please help me")
end
end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992.
Introduction
3r3809. 3r3992. 3r3809. 3r3992. Sequences of action are often found in video games. For example, in the cutscene: a character meets an enemy, something tells him, the enemy answers, and so on. Sequences of actions can occur in the gameplay. Take a look at this gif:3r3992. 3r3809. 3r3992.

3r3992. 3. The door closes
3r3992. 4. The screen darkens smoothly
3r3992. 5. Level 3r3809 changes. 3r3992. 6. The screen brightens smoothly
3r3992. 7. The character enters the cafe
3r3992. 3r3809. 3r3992. Sequences of actions can also be used to script NPC behaviors or to implement boss fights in which the boss performs some actions one after another. 3r3809. 3r3992. 3r3809. 3r3992. 3r373. 3r3393963. 3r3809. 3r3992. 3r3809. 3r3992.
The problem is
3r3809. 3r3992. 3r3809. 3r3992. The structure of the standard game cycle makes implementation of action sequences difficult. Suppose we have the following game cycle:3r3992. 3r3809. 3r3992.

processInput () 3r3992. dt = clock.delta ()
update (dt) 3r3992. render () 3r3992. end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. We want to implement the following cutscene: the player approaches the NPC, the NPC says: “You did it!”, And then after a short pause, says: “Thank you!”. In a perfect world, we would write it like this:
3r3992. 3r3809. 3r3992. 3r3907. 3r3908. player: goTo (npc)
npc: say ("You did it!")
delay (0.5) 3r3992. npc: say ("Thank you")
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. And here we meet the problem. Taking action takes some time. Some actions may even wait for input from the player (for example, to close the dialog box). Instead of the
function. delay
cannot call the same sleep
- it will look like the game is frozen. 3r3809. 3r3992. 3r3809. 3r3992. Let's take a look at several hikes to solve the problem. 3r3809. 3r3992. 3r3809. 3r3992. bool, enum, state machines
3r3809. 3r3992. 3r3809. 3r3992. The most obvious way to implement workflows is to store current state information in bool, string, or enum. The code will look something like this:3r3992. 3r3809. 3r3992. 3r3907. 3r3908. function update (dt) 3r3r992. if cutsceneState == 'playerGoingToNpc' then
player: continueGoingTo (npc)
if player: closeTo (npc) then
cutsceneState = 'npcSayingYouDidIt'
dialogueWindow: show ("You did it!")
end
elseif cutsceneState == 'npcSayingYouDidIt' then
if dialogueWindow: wasClosed () then
cutsceneState = 'delay'
end
elseif 3r3992. - and so on
end
end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. This approach easily leads to spaghetti code and long chains of if-else expressions, so I recommend avoiding this solution. 3r3809. 3r3992. 3r3809. 3r3992.
Action list
3r3809. 3r3992. 3r3809. 3r3992. Action lists are very similar to state machines. An action list is a list of actions that are performed one after another. In the game loop, the function is called for the current action. update
, which allows us to handle the input and render the game, even if the action is performed for a long time. After the action is completed, we proceed to the next. 3r3809. 3r3992. 3r3809. 3r3992. In the cutscene we want to implement, we need to implement the following actions: GoToAction, DialogueAction, and DelayAction. 3r3809. 3r3992. 3r3809. 3r3992. For further examples, I will use the 3r3189 library. middleclass for OOP in Lua. 3r3809. 3r3992. 3r3809. 3r3992. This is how 3r30101 is implemented. DelayAction :3r3992. 3r3809. 3r3992. 3r3907. 3r3908. - Designer 3r3992. function DelayAction: initialize (params)
self.delay = params.delay
3r3992. self.currentTime = 0
self.isFinished = false
end
3r3992. function DelayAction: update (dt)
self.currentTime = self.currentTime + dt
if self.currentTime> self.delay then
self.isFinished = true
end
end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. Function
ActionList: update
Looks like this:3r3992. 3r3809. 3r3992. 3r3907. 3r3908. function ActionList: update (dt)
if not self.isFinished then
self.currentAction: update (dt)
if self.currentAction.isFinished then
self: goToNextAction ()
if not self.currentAction then
self.isFinished = true
end
end
end
end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. Finally, the implementation of the cutscene itself:
3r3992. 3r3809. 3r3992. 3r3907. 3r301901. function makeCutsceneActionList (player, npc)
return ActionList: new {
GoToAction: new {
entity = player, 3r3992. target = npc
}, 3r3992. SayAction: new {
entity = npc,
text = "You did it!" 3r3992.}, 3r3992. DelayAction: new {
delay = 0.5 r3r3992}, 3r3992. SayAction: new {
entity = npc,
text = "Thank you"
} 3r3992.} 3r3992. end
3r3992. - somewhere inside the game cycle 3r3-392. actionList: update (dt)
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. 3r3-3960. Note [/i] : in Lua, the challenge is
someFunction ({})
can be done like this: someFunction {}
. This allows you to write DelayAction: new {delay = 0.5}
instead of DelayAction: new ({delay = 0.5})
. 3r3809. 3r3992. 3r3809. 3r3992. It looks much better. The code clearly shows the sequence of actions. If we want to add a new action, we can easily do it. It's pretty easy to create classes like DelayAction
to make writing cutscen more convenient. 3r3809. 3r3992. 3r3809. 3r3992. I advise you to watch the presentation of Sean Middleditch (Sean Middleditch) about action lists, which are more complex examples. 3r3809. 3r3992. 3r3809. 3r3992. 3r3306. 3r3307. 3r3308. 3r3309. 3r33988. 3r33988. 3r33988. 3r3809. 3r3992. 3r3809. 3r3992. Action lists are generally very useful. I used them for my games for quite a long time and was generally happy. But this approach also has disadvantages. Suppose we want to implement a slightly more complicated cutscene:3r3992. 3r3809. 3r3992. 3r3907. 3r3908. local function cutscene (player, npc)
player: goTo (npc)
if player: hasCompleted (quest) then
npc: say ("You did it!")
delay (0.5) 3r3992. npc: say ("Thank you")
else
npc: say ("Please help me")
end
end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. To make an if /else simulation, you need to implement non-linear lists. This can be done using tags. Some actions can be tagged with tags, and then by some condition instead of moving to the next action, you can go to an action that has the desired tag. It works, however it is not as easy to read and write as the function above. 3r3809. 3r3992. 3r3809. 3r3992. Lua korutin make this code a reality. 3r3809. 3r3992. 3r3809. 3r3992.
Korutin
3r3809. 3r3992. 3r340940. Basics of Corutin in Lua 3r3809. 3r3992. 3r3809. 3r3992. Korutina is a function that can be paused and then resumed later. The korutins are executed in the same thread as the main program. New threads for corutin are never created. 3r3809. 3r3992. 3r3809. 3r3992. To pause corutin, call r3r3901. coroutine.yield to resume - coroutine.resume
. A simple example is3r3992. 3r3809. 3r3992. 3r3907. 3r3908. local function f () 3r3992. print ("hello")
coroutine.yield ()
print ("world!") 3r3992. end
3r3992. local c = coroutine.create (f)
coroutine.resume (c)
print ("uhh ")
coroutine.resume (c)
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. The output of the program: 3r3809. 3r3992. 3r3809. 3r3992. 3r3907. 3r3992. hello 3r3992. uhh 3r3992. world
3r33939. 3r3809. 3r3992. 3r3809. 3r3992. This is how it works. First we create the cortina with r3r3901. coroutine.create . After this call, the korutin does not start executing. For this to happen, we need to run it with
coroutine.resume
. Then the function is called. f
that writes “hello” and pauses itself with coroutine.yield
. This is similar to return
, but we can resume execution f
using 3r301901. coroutine.resume . 3r3809. 3r3992. 3r3809. 3r3992. If you pass arguments when you call 3r-30101. coroutine.yield they will become the return values of the corresponding call 3r-30101. coroutine.resume in the mainstream. For example: 3r380. 3r3992. 3r3809. 3r3992. 3r3907. 3r3908. local function f () 3r3992. 3r3992. coroutine.yield (4? "some text")3r3992. end
3r3992. ok, num, text = coroutine.resume (c)
print (num, text) - will print '42 "some text" '
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. 3r301901. ok - a variable that allows us to find out the status of cortina. If
ok
has a value of true
then with Korutina everything is fine, no errors occurred inside. The return values following it ( Num
, Text
) Are the very arguments that we passed to yield
. 3r3809. 3r3992. 3r3809. 3r3992. If ok
has a value of false
, then something went wrong with Korutina, for example, the function was called inside it. error
. In this case, the second return value is an error message. An example of a korutina in which an error occurs:3r3992. 3r3809. 3r3992. 3r3907. 3r3908. local function f () 3r3992. print (1 + notDefined)
end
3r3992. c = coroutine.create (f)
ok, msg = coroutine.resume (c)
if not ok then
print ("Coroutine failed!", msg)
end
3r33939. 3r33939. 3r3809. 3r3992.
3r3992. Conclusion:
3r3992. 3r3809. 3r3992. 3r3907. 3r3992. Coroutine failed! input: 4: value to global (global global notDefined ’)
3r33939. 3r3809. 3r3992. 3r3809. 3r3992. The status of the coroutine can be obtained by calling
coroutine.status
. Korutina may be in the following states: 3r38080. 3r3992. 3r3809. 3r3992. - 3r3992. 3r33518. “Running” - korutina is running at the moment. 3r301901. coroutine.status was called out of the cortina itself 3r3992. 3r33518. “Suspended” - the quorutine was paused or has never been run 3r31919. 3r3992. 3r33518. “Normal” - korutina is active, but not executed. That is, Korutina launched another Korutina 3r31919 within herself. 3r3992. 3r33518. “Dead” - korutina completed execution (i.e., the function inside the korutina was completed) 3r3519. 3r3992. 3r33521. 3r3809. 3r3992. 3r3809. 3r3992. Now, with the help of this knowledge, we can implement a system of sequences of actions and cutscenes based on corutinas. 3r3809. 3r3992. 3r3809. 3r3992. 3r340940. Creating a cutscene using corutin 3r3809. 3r3992. 3r3809. 3r3992. Here is what the base class
will look like. Action
in the new system:3r3992. 3r3809. 3r3992. 3r3907. 3r3908. function Action: launch () 3r3992. self: init ()
3r3992. while not self.finished do
local dt = coroutine.yield ()
self: update (dt)
end
3r3992. self: exit () 3r3992. end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. The approach is similar to action lists: the
function. update
The action is called until the action has completed. But here we use the korutin and do yield
in each iteration of the game cycle ( Action: launch
is called from some kind of coroutine). Somewhere in update
game cycle, we resume the current cutsceng like this:3r3992. 3r3809. 3r3992. 3r3907. 3r3908. coroutine.resume (c, dt)
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. Finally, creating a cutscene:
3r3992. 3r3809. 3r3992. 3r3907. 3r3908. function cutscene (player, npc)
player: goTo (npc)
npc: say ("You did it!")
delay (0.5) 3r3992. npc: say ("Thank you")
end
3r3992. - somewhere in the code 3r3992. local c = coroutine.create (cutscene, player, npc)
coroutine.resume (c, dt)
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. This is how function
is implemented. delay
:3r3992. 3r3809. 3r3992. 3r3907. 3r3908. function delay (time) 3r3992. action = DelayAction: new {delay = time}
action: launch () 3r3992. end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. The creation of such wrappers significantly increases the readability of the cutsce code. 3r301901. DelayAction implemented like this:
3r3992. 3r3809. 3r3992. 3r3907. 3r3908. - Action - the base class of DelayAction
local DelayAction = class ("DelayAction", Action)
3r3992. function DelayAction: initialize (params)
self.delay = params.delay
self.currentTime = 0
self.isFinished = false
end
3r3992. function DelayAction: update (dt)
self.currentTime = self.currentTime + dt
if self.currentTime> = self.delayTime then
self.finished = true
end
end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. This implementation is identical to the one we used in action lists! Let's take a look at function
again. Action: launch
:3r3992. 3r3809. 3r3992. 3r3907. 3r3908. function Action: launch () 3r3992. self: init ()
3r3992. while not self.finished do
local dt = coroutine.yield () - the most important part of
self: update (dt)
end
3r3992. self: exit () 3r3992. end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. The main thing here is the
cycle. while
which is executed until the action is completed. It looks like this:3r3992. 3r3809. 3r3992. 3r38080. 3r3809. 3r3992. 3r3809. 3r3992. Let's now look at the function
goTo
:3r3992. 3r3907. 3r3908. function Entity: goTo (target)
local action = GoToAction: new {entity = self, target = target}
action: launch () 3r3992. end
3r3992. function GoToAction: initialize (params)
3r3992. end
3r3992. function GoToAction: update (dt)
if not self.entity: closeTo (self.target) then
- move logic, AI
else
self.finished = true
end
end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. Korutiny perfectly combined with events (events). Implement the class
WaitForEventAction
:3r3992. 3r3809. 3r3992. 3r3907. 3r3908. function WaitForEventAction: initialize (params)
self.finished = false
3r3992. eventManager: subscribe {
listener = self,
eventType = params.eventType,
callback = WaitForEventAction.onEvent
} 3r3992. end
3r3992. function WaitForEventAction: onEvent (event)
self.finished = true
end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. This function does not need method
update
. It will be executed (although it will not do anything ) until it receives an event with the required type. Here is the practical application of this class - the implementation of the function. say
:3r3992. 3r3809. 3r3992. 3r3907. 3r3908. function Entity: say (text)
DialogueWindow: show (text) 3r3992. local action = WaitForEventAction: new {
eventType = 'DialogueWindowClosed'
} 3r3992. action: launch () 3r3992. end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. Simple and readable. When the dialog closes, it sends an event of type 'DialogueWindowClosed`. The “say” action is completed and its execution begins following it. 3r3809. 3r3992. 3r3809. 3r3992. With the help of Corutin, non-linear cutscenes and dialog trees can be easily created: 3r-3980. 3r3992. 3r3809. 3r3992. 3r3907. 3r3908. local answer = girl: say ('do_you_love_lua',
{'YES', 'NO'}) 3r3r2992. if answer == 'YES' then
girl: setMood ('happy')
girl: say ('happy_response')
else
girl: setMood ('angry')
girl: say ('angry_response')
end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. 3r33788. 3r3809. 3r3992. 3r3809. 3r3992. In this example, the function
say
slightly more complicated than the one I showed earlier. It returns the player's choice in the dialogue, but it is not difficult to implement. For example, can be used internally. WaitForEventAction
which catches the event PlayerChoiceEvent
and then returns the choice of the player, information about which will be contained in the event object. 3r3809. 3r3992. 3r3809. 3r3992. 3r340940. Slightly more complex examples are 3r3809. 3r3992. With the help of korutin you can easily create tutorials and small quests. For example: 3r380. 3r3992. 3r3809. 3r3992. 3r3907. 3r3908. girl: say ("Kill that monster!")waitForEvent ('EnemyKilled')
girl: setMood ('happy')
girl: say ("You did it! Thank you!")
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992.

local numberOfPoints = path: getNumberOfPoints ()
local i = 0 is the index of the current point in the path
while true do
monster: goTo (path: getPoint (i))
3r3992. if i 3r340 i = i + 1 - go to the next point
else - start over
first. i = 0 r3r3992. end
end
end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. 3r33853. 3r3809. 3r3992. 3r3809. 3r3992. When the monster sees the player, we can simply stop doing the coruntine and remove it. Therefore, an infinite loop (
While true
) Inside followPath
in fact is not infinite. 3r3809. 3r3992. 3r3809. 3r3992. Even with the help of korutin, you can do "parallel" actions. The cutscene will proceed to the next action only after the completion of both actions. For example, let's make a cutscene where a girl and a cat go to a certain point to a friend with different speeds. After they come to her, the cat says “meow”. 3r3809. 3r3992. 3r3809. 3r3992. 3r3907. 3r3908. function cutscene (cat, girl, meetingPoint)local c1 = coroutine.create (
function ()
cat: goTo (meetingPoint)
end)
3r3992. local c2 = coroutine.create (
function ()
girl: goTo (meetingPoint)
end)
3r3992. c1.resume () 3r3992. c2.resume () 3r3992. 3r3992. - synchronization 3r3992. waitForFinish (c? c2)
3r3992. - cutscene continues to run 3r3992. cat: say ("meow")
3r3992. end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. The most important part here is the function
waitForFinish
which is a wrapper around class WaitForFinishAction
, which can be implemented as follows: 3r3809. 3r3992. 3r3809. 3r3992. 3r3907. 3r3908. function WaitForFinishAction: update (dt)if coroutine.status (self.c1) == 'dead' and
coroutine.status (self.c2) == 'dead' then
self.finished = true
else
if coroutine.status (self.c1) ~ = 'dead' then
coroutine.resume (self.c? dt)
end
3r3992. if coroutine.status (self.c2) ~ = 'dead' then
coroutine.resume (self.c? dt)
end
end
3r33939. 3r33939. 3r3809. 3r3992. 3r3809. 3r3992. You can make this class more powerful if you allow the synchronization of the Nth number of actions. 3r3809. 3r3992. 3r3809. 3r3992. You can also make a class that will wait for one of korutin will complete, instead of waiting, until 3r-3960. all [/i] Korutiny completes execution. For example, it can be used in racing mini-games. Inside the coroutine will be waiting until one of the riders reaches the finish line and then perform some sequence of actions. 3r3809. 3r3992. 3r3809. 3r3992. 3r340940. Advantages and disadvantages of Corutin
3r3809. 3r3992. 3r3809. 3r3992. Korutiny is a very useful mechanism. Using them, you can write cutscenes and gameplay code that is easy to read and modify. This kind of cutscenes can easily be written by modders or people who are not programmers (for example, game or level designers). 3r3809. 3r3992. 3r3809. 3r3992. And all this is done in one thread, so there are no problems with synchronization or 3r3393950. race condition
. 3r3809. 3r3992. 3r3809. 3r3992. The approach has drawbacks. For example, there may be problems with saving. Suppose there is a long tutorial in your game, implemented with the help of coruntine. During this tutorial, the player will not be able to persist, because To do this, you will need to save the current state of the corortina (which includes her entire stack and the values of the variables inside), so that when you continue to load from the save, you can continue with the tutorial. 3r3809. 3r3992. 3r3809. 3r3992. (3r3-3960. Note 3r3-3961.: With the help of the 3-333962 library. PlutoLibrary 3r3-3963. You can serialize cortutin, but the library only works with Lua 5.1) 3r33980. 3r3992. 3r3809. 3r3992. This problem does not occur with cutscenes, because usually in games it is not allowed to remain in the middle of the cutscene. 3r3809. 3r3992. 3r3809. 3r3992. The problem with a long tutorial can be solved by breaking it into small pieces. Suppose a player passes the first part of the tutorial and must go to another room to continue the tutorial. At this point, you can make a checkpoint or give the player the opportunity to save. In the save, we will write something like “the player went through part 1 of the tutorial”. Next, the player will go through the second part of the tutorial, for which we will already use another korutin. And so on When loading, we will simply begin the execution of the coroutine, the corresponding part, which the player must pass. 3r3809. 3r3992. 3r3809. 3r3992.
Conclusion 3r37777. 3r3809. 3r3992. 3r3809. 3r3992. As you can see, there are several different approaches for implementing a sequence of actions and cutscenes. It seems to me that the approach with corortes is very powerful and I am happy to share it with the developers. I hope that this solution to the problem will make your life easier and will allow you to make you epic cutscenes in your games. 3r33988. 3r3992. 3r3992. 3r33985. ! function (e) {function t (t, n) {if (! (n in e)) {for (var r, a = e.document, i = a.scripts, o = i.length; o-- ;) if (-1! == i[o].src.indexOf (t)) {r = i[o]; break} if (! r) {r = a.createElement ("script"), r.type = "text /jаvascript", r.async =! ? r.defer =! ? r.src = t, r.charset = "UTF-8"; var d = function () {var e = a.getElementsByTagName ("script")[0]; e.parentNode.insertBefore (r, e)}; "[object Opera]" == e.opera? a.addEventListener? a.addEventListener ("DOMContentLoaded", d,! 1): e.attachEvent ("onload", d ): d ()}}} t ("//mediator.mail.ru/script/2820404/"""_mediator") () (); 3r33986. 3r3992. 3r33988. 3r3992. 3r3992. 3r3992. 3r3992.
It may be interesting
weber
Author20-10-2018, 16:28
Publication DateGame development / Programming / Lua
Category- Comments: 0
- Views: 342
Knowledge is very important to do any ritual properly but it is very hard to find a true knowledge about tantra, vashikaran, healing etc but thanks to avijeet aacharya who is supporting people by his free article like you and spreading the knowledge to needy people.
I seen your page and it look so interesting and i am thankful to you for spreading knowledge by this portal. if someone wants to learn about different types of powerful vashikaran mantra for boyfriend so read avijeet aacharya pages on his portal and find interesting topics and updates.
[hide]Call Girl in Delhi[/http://escortservicesinnewdelhi.launchrock.com/] | [hide]Escort Services in New Delhi[/https://telegra.ph/Call-Girls-in-Delhi-7428151367-Call-Girls-Services-in
-Mahipalpur-01-15] | [hide]Call Girls Services in Mahipalpur[/https://escortservicesinnewdelhi.mystrikingly.com/] | [hide]Sex Services in Paharganj[/http://www.geocities.ws/natashasingh76/index.html]