Saturday 21 February 2015

TS2015 - Scenario Scripting in LUA Part 12 - Video

Apologies for the long time between articles!

In this article i'm going to show you how to incorporate videos in to your scenarios, done correctly they can really elevate your scenarios to a completely different level.

The overall API for playing videos is almost identical to the API for playing sounds - so if you haven't yet read the previous blog post on the subject then now would be a great time to do that.

At the top of your LUA script file you're going to want to drop in some definitions, just to make the rest of the script a bit easier to read:

FULLSCREEN = 0
FRONT_AND_CENTER = 1
CALL = 2

FALSE = 0
TRUE = 1

NO_CONTROLS = 0
PLAYPAUSE = 1
STOP = 2
SEEK = 4

Note: You may already have FALSE and TRUE defined, in which case there's no need to duplicate them here.

The calls to handle videos are as follows:

Start a video playing:
SysCall("ScenarioManager:PlayVideoMessage", "2013_startup.ogv", FRONT_AND_CENTER, FALSE, NO_CONTROLS,0)

Check to see if a video is playing:
SysCall("ScenarioManager:IsVideoMessagePlaying", "2013_startup.ogv")

Stop a video from playing:
SysCall("ScenarioManager:StopVideoMessage", "2013_startup.ogv")

Let's put it in a small example to provide some context:

function OnEvent(event)
  _G["OnEvent" .. event]();
end

function OnEventStart()
  SysCall("ScenarioManager:PlayVideoMessage", "2013_startup.ogv", FULLSCREEN, FALSE, NO_CONTROLS,0)
end

Here, i'm defining a trigger event "Start" (which should be initiated from a Trigger instruction in the scenario definition) and this will play a video, full screen.

Just as with audio, the call to play a video will return immediately and let the video carry on playing in the background, that's the main reason for the "IsVideoMessagePlaying" and "StopVideoMessage" calls.  I'll show them in action in a moment.

Parameters

There are a number of parameters to the PlayVideoMessage function that are interesting.

After the Filename, the next one tells the game how and where you want the video displayed.  The options are Fullscreen (0), Front and Center (1), and Video Call (2).  I've defined some constants above so that it's easier to see what it is doing rather than passing plain numbers in (never do that, trust me!).

Full Screen puts the video full screen, just as it says it will.  It will also include an X in the top right that allows the player to abort playback of the video if they wish.  In the screenshot below you can see it has completely filled the game screen.

Full Screen Playback
Front and Center gives you a video playback which does not fill the screen and therefore allows some context from within the game to still be seen, but it is meant to still command the users attention fully and is of sufficient size it can still contain some detail.
Front and Center Playback
Finally, Video Call is intended to show in the top left corner somewhat like if you had a video call going on with someone.  You could use this for giving brief snippets of information, perhaps a news bulletin about something that's just happened (based on the players action) or something similar.
Video Call Playback


The next option is either TRUE (1) or FALSE (0) and this tells the game whether to pause in-game action or leave it running.  If you're going to get in the way of the user such as a full screen video then I'd suggest pausing the game is a good idea.  If it's a video call, then you probably don't want to pause it so that it feels more like something that is happening as part of the game play.

The next option tells the game what kinds of controls to put on the video, do you want them to be able to pause it? skip to a particular bit? or stop it?  There are four constants defined for this in my example.  NO_CONTROLS (0) means show nothing, and is used in all the above three screenshot examples.  PLAYPAUSE (1), STOP (2) and SEEK (4) add the relevant controls that you would expect.  If you want to add more than one control, simply add them together like this:

SysCall("ScenarioManager:PlayVideoMessage", "2013_startup.ogv", FRONT_AND_CENTER, FALSE, (PLAYPAUSE+STOP+SEEK),0)

This would give you something like this::

Front and Center with all controls enabled along the bottom of the video
The final parameter is always a zero.

Format

The format of the video is an Ogg Vorbis ".ogv" video.  You will need to create your video and export it in some format using your preferred editing tool and then if it won't export to Ogv then you'll need to use an additional step to convert it.  As a reference example, the 2013_startup.ogv used as part of the start-up sequence of the game is in the correct format.  You can use this to confirm you're calling the video correctly and it is working, and then drop your own video in afterwards.  You can find this video in the data\videos folder.

I use FFMpeg2Theora to do my conversions, available here, once installed you can run it with this command line to get the right format:

ffmpeg2theora-0.29.exe myvideo.mp4 -vcodec libtheora -acodec libvorbis -v 10

Here i've used an mp4 video because that's what I exported from my video editing suite, the result will go out as myvideo.ogv .

The number at the end is the quality level, 10 is the maximum and reducing it can make a huge difference to the file size - as well as a huge difference to the quality.  You will likely find that you can drop it down one or two settings before you even notice a difference and then it will drop off quickly.  Find that right setting for your video that means you can get the file size down as far as possible while still remaining good quality.

Place the videos in the localised language folder, the same as the audio files.  So this would be in the "en" folder for English users, for example.

Bigger Example

So here's a bigger example showing how you can use conditions to work out when to continue with the scenario.

function OnEvent(event)
  _G["OnEvent" .. event]();
end

function TestCondition(condition)
  _G["TestCondition" .. condition]();
end

function OnEventStart()
  videoPlaying = SysCall("ScenarioManager:IsVideoMessagePlaying", "2013_startup.ogv")
  if (videoPlaying == TRUE) then
    SysCall("ScenarioManager:StopVideoMessage", "2013_startup.ogv")
  end
  SysCall("ScenarioManager:PlayVideoMessage", "2013_startup.ogv", FULLSCREEN, FALSE, NO_CONTROLS,0)
  SysCall("ScenarioManager:BeginConditionCheck", "HasItStoppedYet")
end

function OnEventNextStep()
  SysCall("ScenarioManager:ShowInfoMessageExt", "Get moving", "moving.html", 10, MSG_TOP + MSG_TOP, MSG_SMALL, TRUE);
end

function TestConditionHasItStoppedYet()
  videoPlaying = SysCall("ScenarioManager:IsVideoMessagePlaying", "2013_startup.ogv")
  if (videoPlaying == FALSE) then
    OnEventNextStep()
  end
end

This example is somewhat contrived to try and fit a few different things in to a small space but it should give you the "snippets" to do what you want to do.

In the scenario instructions we're triggering "Start", which ultimately calls OnEventStart().
In OnEventStart() we find out if the video is currently playing and if so, we stop it.  It's highly unlikely that would be the case here, but normally you might find a place where there's a chance a video is already playing and now as a result of a more gameplay based event you need to play a video so the first thing you do is make sure that nothing else is playing first.
We then kick off playing our video, and then finally start a condition check called HasItStoppedYet.

The game will now play the video, and while the user is enjoying this video the game will now be calling TestConditionHasItStoppedYet over and over again, each time it will find out if the video is currently playing.  At some point this is going to return FALSE because our video finally finished playing and when this happens we manually trigger another event NextStep.

When OnEventNextStep runs, it displays a message to the user, presumably saying "right, now get moving".  Had you just tried to put the message box up, it would have appeared over or simply interrupted the video.  In this way, you remain fully in control of when things are presented to the user.











4 comments:

  1. I read your whole article, it's really interesting and helpful thanks for sharing. PNR status
    Train running status

    ReplyDelete
  2. SLT Train Sim Live, first of all thank you and congratulations for the richness of this blog that can allow fans of TS20 XX to understand, know the principles and processes of different key points and technical terms in TS all explained and rendered very understanding even for the most profane of us which shows a very pedagogical aspect of your way of doing things (sorry I do not want to play the psycho ;-)) anyway thank you for all these explanations and demonstrations for me in any case was a great help in the realization of a project that was important to me and so I thank you. I sincerely hope that you can continue to share your knowledge and your passion to the many fans of TS who I'm sure you would appreciate your blog if it was more indexed, I fell on it really by chance but is not released since! ;-) Good luck to Train Sim Live! Sincerely, Tof
    PS: Sorry for the translation, lol, ;-)

    ReplyDelete
  3. It seems that this doesn't work anymore since the last TS major update and patches after that.
    The video window doesn't appear at all.

    Last time (last january) I created a scenario with a video in it and all was working fine.
    I now updated the scenario without any changes in scenarioscript.lua and the video doesn't work anymore.

    The only thing I can find in logmate that seems relevant is this:
    2023-08-01 13:33:04.804 - [Steam] - Trace D:\build\CoreRelease\Code\DLLs\ScenarioManager\cCareerModel.cpp : 458 = cCareerModel::OnStatsUploaded 24010 1
    2023-08-01 13:33:04.804 - [Scenario Manager] - Trace D:\build\CoreRelease\Code\DLLs\ScenarioManager\cDriverInstructionOrProp.cpp : 214 = Instruction satisfied 1
    2023-08-01 13:33:04.804 - [RunTimeError] - Verify failed:
    2023-08-01 13:33:04.804 - [RunTimeError] -
    2023-08-01 13:33:04.804 - [RunTimeError] - D:\build\CoreRelease\Code\DLLs\ScenarioManager\cScenarioManager.cpp : 3921
    2023-08-01 13:33:04.804 - [RunTimeError] -
    2023-08-01 13:33:04.804 - [RunTimeError] - Expression: scriptPtr->PopResult ( retVal )
    2023-08-01 13:33:04.804 - [RunTimeError] -
    2023-08-01 13:33:04.804 - [RunTimeError] -
    2023-08-01 13:33:04.804 - [RunTimeError] - Verify failed:
    2023-08-01 13:33:04.804 - [RunTimeError] -
    2023-08-01 13:33:04.804 - [RunTimeError] - D:\build\CoreRelease\Code\DLLs\WindowsManager\cMoviePlayerDialog.cpp : 134
    2023-08-01 13:33:04.804 - [RunTimeError] -
    2023-08-01 13:33:04.804 - [RunTimeError] - Expression: ratio >= 0.0f && ratio <= 1.0f
    2023-08-01 13:33:04.804 - [RunTimeError] -
    2023-08-01 13:33:04.804 - [RunTimeError] -

    ReplyDelete
  4. Just now got a reply from support.
    Turns out the whole videoplayer has been removed. According to them it had been known to cause crashes.

    Never had any problems with it, besides when I screwed up my lua code myself in scenarioscript.lua.
    And crashes due to screw-ups like that don't only apply to the videoplayer.

    It's a big shame that a function like the videoplayer has been phased out.
    It could bring so much more to a scenario.

    ReplyDelete