Saturday 7 February 2015

TS2015 - Scenario Scripting in LUA Part 5 - Cinematic Cameras

In todays article, I'm going to cover one of the most exciting features you can access through LUA scripting in your scenarios, the use of Cinematic Cameras.  These cameras allow you to create specific sequences that might commonly be used to set a scene at the start of a scenario, or perhaps even during a scenario they can be used to indicate some thing that's going on in the world around the player.

In addition to the Cinematic Cameras, I'll also then cover how you can access the other fixed cameras such as forcing cab view or track side view for example.

If you have not yet watched the YouTube video from the TSL show segment that covered this then I would recommend you do this first because it's always easier to see things visually.

Click here to watch the episode

There are a couple of steps to using the cinematic cameras:

1. Place and set up the cameras to create the sequence itself
2. Start the camera sequence from LUA script

Setting up the Camera


Most of how you place the camera is far better explained visually in the video but here's a route outline of what you need to do.


First, you need to place a Cinematic Camera asset, which you can find on the middle-left flyout under the "miscellaneous" group, which is the bag in the bottom right of the little group of icons.


First, you need to place a Cinematic Camera asset, which you can find on the middle-left flyout under the "miscellaneous" group, which is the bag in the bottom right of the little group of icons.







Double click on the camera asset to get access to its properties on the top right fly-out.

You can use one of the arrow icons (third one along) to move the camera so that it is pointing to exactly what you are currently looking at, this is by far the easiest way to position the camera.

You can change the field of view by changing the value in the fifth field down (it defaults to 65) to get a narrower angle by lowering it, or a more wide angle by raising it.

Click the "+" icon (second icon) to add a new key frame and then use the left/right yellow and white arrows on the top right to move between the key frames.  It tells you below the arrows which frame you're on, e.g. 3/4 means third out of four.  You can use the "bin" icon (first one) to delete the current key frame.

Once you've positioned all the cameras, go back and set times using the sixth field down - so for key frame 1, the time means how long to spend on that key frame before advancing to the next.

Once you've finished setting up all the key frames for your cinematic sequence, you can test it out right away by clicking on the "play" icon at the bottom.  This will run through the sequence and you can use the clock on the bottom right of the fly-out to watch as the sequence runs through it's timings to make sure everything works out as you planned it.

Once you start reviewing the sequence with "play" it is important to make sure that you click "stop" to end it as many of the fields are not accessible while it is playing.  You can use the rest of the controls along the bottom such as rewind and pause the same way you would expect to.

Once you're happy with the sequence, put a name in to the bigger text field (with the cube next to it) - this is the name that we'll use to start the sequence off.

Starting the Sequence


At this point, we're finished with the cinematic camera editor and can now return to the scenario editor and write a little LUA code to activate it.

In this example, let's assume it's an opening camera sequence.  I named the sequence "coolopening" in the cinematic camera name box described above, and in the scenario i've added a trigger box which triggers an event "Start".

Let's see the code that now makes the magic happen.

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

function OnEventStart()
  SysCall ( "CameraManager:ActivateCamera", "coolopening", 0 );
end

So we start out with our standard "OnEvent" function which passes it on to a separate function to handle the specific event - in this case, OnEventStart, because the first event we triggered was called "Start".

Inside that event, we then call the ActivateCamera function in the game and ask it to run "coolopening"; the name of our cinematic camera sequence.

That's really all there is to getting it working, but there are some extra bits that are worth knowing.

Delaying the next instruction


If you want to run a camera sequence and then do something else (which you most likely will!) then make sure that the trigger instruction has a timed offset.  This is because the command to run a camera sequence actually finishes immediately and returns control back to the game to run the next instruction in the scenario, while at the same time it runs the camera in parallel.  If you don't put a delay on the next instruction it can result in the camera sequence being interrupted.


The example above shows that in the first field of the trigger instruction, i've set up a 15 second delay - which means that from the moment this instruction would normally execute, it will hold off for 15 seconds.  If our first instruction was to fire a camera sequence that lasted 15 seconds, this would mean that the instruction above waits for 15 seconds and then fires another trigger to continue the script on to the next step.  You will simply need to add up the total time of your cinematic sequence (including the time on the last key frame) and then put that in to the delay box.

Chaining multiple camera sequences together


If you want to chain two camera sequences together, e.g. to do a pass through of a platform and then another pass over the player train then you simply set them up as normal and make sure that the trigger instruction which causes the second has a suitable delay so that it will not trigger before the first one has finished running.

Accessing Internal Cameras


There are a number of internal cameras defined that you can refer using the ActivateCamera call. Note that these names must be entered precisely, including the correct capitalisation.

CabCameraSwitches to the cab camera (like pressing 1 normally)
ExternalCameraSwitch to view "2"
TrackSideCameraSwitch to view "4"
CarriageCameraSwitch to view "5"
CouplingCameraSwitch to view "6"
YardCameraSwitch to view "7"
HeadOutCameraSwitch to view "Shift-2"
FreeCameraSwitch to view "8"

Helper Functions

All good coders know the importance of keeping your code clean, and one of the important ways of doing this is to spread your code in to different functions that look after different aspects.  One key area you can do this, and also greatly simplify reading of your code later, is to create some short helper functions that wrap the SysCall's, something like this:

function ShowCamera(camera)
  SysCall ( "CameraManager:ActivateCamera", "coolopening", 0 )
end

function ShowCabCamera()
  ShowCamera("CabCamera")
end

function ShowTracksideCamera()
  ShowCamera("TrackSideCamera")
end

It might seem redundant to have "ShowCabCamera" simply call ShowCamera when it could simply call the SysCall itself, this is a style choice you can make.  Personally I like to keep duplication to a minimum and the cost of a function call in these situations is minimal, so the code remains nicely readable and there's only one place that actually calls the game.

Having built yourself a nice library of helper functions (and this is a process you can apply to everything we talk about in these posts) your actual main code becomes quite a bit more readable:

function OnEventStart()
  ShowCamera("coolopening")
end

Recorded Messages and Cinematic Cameras


It is perfectly possible, and some times quite desirable, to link playing a cinematic camera with a recorded message.  It's important to remember to reset the camera back to the cab when you do this, as shown in the following example where I want to have a cinematic highlight a road traffic accident that the player is driving past.  I would have set up the scene using scenario-specific assets, then put the camera on it to fly around it a bit.  I could then call it like this:

function StartDisplayRTA()
  ShowCamera("RTACamera")
end

function StopDisplayRTA()
  ShowCabCamera()
end

function OnEventShowRTA()
  DisplayRecordedmessage("RTA")
end

Here you can see i'm continuing to use the new library of functions i've built up previously and the code is immediately far more readable than a bunch of SysCall's all over the place.  In this case the cinematic will play as normal and when it finishes it will return to the cab.  If the player then clicks on the "previous message" button on the HUD then it will simply re-play the cinematic and when finished go back to the cab.

As mentioned in an earlier article, you really can use the Recorded Message function for all kinds of things, not just messages.




1 comment:

  1. Is there a way to get rid of the HUD while the cinematic plays?

    ReplyDelete