Sunday, 9 March 2014

Motionbuilder : Creating a 3rd Person Game Camera System

If your game engine stores camera settings in a Python parsable language, for example JSON or xml, you may be able to port your engines camera system into Motionbuilder. 

The idea is to give animators the exact same cameras from game, in Mobu and with the same control (XBox pad). Gabriel Ware did a very nice tutorial on creating a 1st person camera system a few years ago, and I thought it would be nice to share my findings on creating a 3rd person camera setup

Although I wont go directly into the math behind our camera system at work, I will show a simplified version, with all the relevant systems in place, and it should be obvious by the end of this tutorial how to extend the system, or adapt it for your own needs.

Preparing the scene
  • From the Asset Browser create 2 Nulls and a Camera. Name the Nulls 'GameCameraPivot' and 'GameCameraLookAt', and name the camera GameCamera
  • In your camera settings, set the Interest to GameCameraLookAt
  •  Drag a joystick from the Devices into the viewport, and turn online
  •  Bring your character into the scene. For this example I will be using Aragor from the tutorial files

Creating Macro Constraints
Macro constraints are basically networks of nodes condensed into a single node, that can then be reused in other relation constraints. They are created exactly the same way as a normal relation constraint would be made, however instead of giving them an input and an output target node, we use the [Macro Tools] to define inputs and outputs. If its confusing for the moment don't worry, I'll go over how you can make one in a few easy steps.

Circle Maker Relation Constraint Macro
Our first macro is an easy one. We want to give the macro a number which we will feed into a sine and cosine function. We then want to multiply both results by a user defined multiplier, and have these values returned from the node.
  • Insert relation constraint, name it Mcr_CircleMaker
  • Double click your new relation constraint and drag in the following nodes, and arrange as shown below.
    • [Macro Tools] : Macro Input Number(x2), Macro Output Number(x2)
    • [Number] : Cosine, Sine, Multiply(x2)

Incrementer Relation Constraint Macro
The point of this macro constraint will be to take an input from a controller's analog stick, and repeatedly  increment or decrement a counter stored in the node. The value will be incremented when the stick is held to the left and decremented when held to the right. 
  1. An analog stick has a range of 0 - 100 and at resting position it usually has a value around 50, we need to take this range and change it to between -1 and 1, giving us a resting point around 0. 
  2. Next we want to determine whether to increment or decrement the value stored in the node. For this we use a greater than or less than, with values set to -0.3 for less than, and 0.3 for more than.
  3. When either is true we want to fire the output of the active node into a pulse node. 
  4. This Pulse node will in turn repeatedly feed 1,0,1,0... into a Triggered Plus Minus Counter Node, which will keep track of  our output value.
    • We can also add 2 more inputs here to be able to control if our output will cycle, and the increment/decrement amount
  • Create another relation constraint and name this Mcr_Incrementer. As before layout the constraint as below, for this constraint you will need,
    • [Macro Tools] : Macro Input Bool, Macro Input Number(x2), Macro Output Number
    • [Number] : Multiply, Subtract, Is Greater, Is Less
    • [Sources] : Pulse(x2)
    • [Other] : Triggered Plus Minus Counter

Game Camera Theory
We now have the basic building blocks for our camera system, and now is probably a good time to take a look at what exactly we want to achieve. We want to be able to place the character in position on screen, and as the user rotates around the player we want our system to keep the player in that area of the screen. 

Below you can see 3 basic variations of this, player left, centre, and right.

 To do this, we need to not only rotate the GameCamera around the GameCameraPivot, but also the camera's GameCameraLookAt around the GameCameraPivot. We will offset the rotation amount between the two by 90 degrees, and this will give us the desired effect. To illustrate more clearly what I'm rambling on about take a look below. 

Notice that both the camera, and its interest are rotating around our GameCameraPivot. 

Building the Master Constraint
  • Create another relation constraint called GameGameraMaster. 
    • Drag the GameCameraPivot, Joystick and your characters root node (for this example I'll use the HipsEffector) into the constraint as senders. In this example I have dragged in the GameCameraPivot in as a sender 3 times. This is to show connections easier, you can however just drag it in once and use the same sender box for all 3 connections.
    • Drag the GameCameraPivot, GameCamera, and GameCameraLookAt into the constraint as receivers.
  • You will need the following utility nodes, and once again arrange as shown below.
    • [My Macros] : Mcr_CircleMaker(x2), Mcr_Incrementer
    • [Converters] : Vector to Number, Number to Vector(x3)
    • [Number] : Subtract
    • [Vector] : Add(x3)
(click for bigger picture)
  •  We now want to rename some boxes so we can have easier access to them through Python. Your boxes may have different default names, but just modify the below code to work with your constraint.
(click for bigger picture)

Creating a Camera Switch Tool
So now we have our constraint setup it would be nice to have a bunch of different settings that the user can select from (eg, defaultCamera, overShoulderShootingCamera, coverCamera). To do this we will store our settings in a JSON file, and write a simple tool to apply these settings. This makes sharing camera settings between multiple users really easy, and insures everyone has the latest up to date camera settings, since game design have a tendency to tinker with them now and again.

This is an example JSON file you could use to store your data.
As long as you build your python functionality to accommodate all the settings you want to set, you shouldn't have any problems. Here is my basic setup, to handle the previous JSON example.

You should now have a simplistic but functional game camera setup in Mobu. This system can be as simple or as complex as you need it to be.

If you have the ear of a gameplay/camera programmer talk with them about the math they use. Chances are you will be able to re-create it in Mobu using the above methods. If your engine stores game camera data in JSON or xml format, you might even be able to directly read in the camera settings from your build.

Although I didn't cover it in this tutorial if you would like to create a nice UI for this tool check out these super useful blogs on PyQt :