| Part 2: Creating A Third Person Camera Back to the overview |
|||||||
A good camera system is very important to any game. In Virtools Dev, you can easily set up a camera following your character using the Keep At Constant Distance BB and the Look At BB. However, it’s hard to create a mouse controlled system that avoids going through objects and doesn’t change the players aim in the process. For an action-packed 3D game it’s absolutely necessary to have an accurate aim! We’ll begin by creating a 3D Frame that is orientated by mouse displacement. Next we will take the direction and position that are calculated in this script and use them to set the orientation and position of the dummy character, the real character and the camera. To give the player a good view of the action it is necessary to adjust the camera distance when the player is looking far down or up (we don’t want to aim at the characters head). We’ll also import a dummy object for the camera to detect if objects are nearby. Collision detection on this object will let us adjust the camera position even more to avoid that the camera is stuck behind walls or slides through objects. The Get Mouse Displacement BB and the Set (Euler) Orientation BB make up the core of the camera-system. The user will learn how to implement and adjust data calculated in one part of a script into another part of the script. We will make extensive use of parameter shortcuts and parameter operations. This tutorial will give the user a good understanding of the hierarchy of objects that makes up the core of the control and camera system. We will also try to explain how priority issues can influence the smoothness of the motion. To make camera adjustments we’ll use Bezier Transform BBs and Bezier Interpolator BBs next to using a dummy object with a Collision Detection BB for proximity testing. All this information is then combined using a Set Component BB to calculate the final position and orientation of the camera. You can continue with your own project or open tutorial_part_2_start.CMO.
|
|||||||
1 Creating A 3D Frame We’ll start by creating a 3D frame. Use the 3D Frame setup to position it at (0,0,0) and rename it “direction_frame”. Check “Show in player mode” and click Set IC. Add a script to this frame by selecting it and pressing “S” in the Level Manager. We will use the script on this frame to calculate all the necessary directions for our character and camera system. This keeps all your directions and positions close to each other for easy adjusting and also keeps it easy to monitor since you have the 3D frame in the viewport as a reference.
|
![]() |
||||||
|
2
Get Mouse Displacement BB Now we will create a system to convert the mouse movement to the orientation of our frame.
• Insert a Get Mouse Displacement BB, a Set Component BB and a Set Euler Orientation BB. Create two local integer parameters and call them “x_displacement” and “y_displacement”. Connect them to the corresponding pOuts of the Get Mouse Displacement BB. These will store the mouse movement. • Create two paramOps above the Set Component BB (<Angle> Multiplication <Integer> <Angle>). Create a local angle parameter and call it “camera_speed”. Set its value to 0:0.2. We will multiply the displacement by this value, combine it using the Set Component BB and then use the result to change the orientation of the direction_frame. Insert this parameter in both paramOps angle input. Create a shortcut to y_displacement above the left paramOp and connect it to the integer input. Connect the paramOp angle output into the Set Component BBs Component 1 input (this is actually a float pIn, but angle and float parameters are compatible). Create a shortcut to the x_displacement above the other paramOp, connect it and connect its output to the Component 2 input. The last Component remains “0”. Set the pOut type of the Set Component BB to “Euler Angles” (by double-clicking on the little triangle). | ||||||
| 3
Setting The Orientation Connect the Set Component pOut into the Euler Angles pIn of the Set Euler Angles BB. Create a This parameter (“Alt+T”) above the Referential pIn and connect it. Notice that the x (horizontal displacement) of the mouse translates into an orientation over the frames y-axis, the y displacement becomes the orientation over the x-axis and there isn’t any orientation over the frames z-axis. Connect all BBs. Loop back from the Set Euler Angle BB bOut to the Get Mouse Displacement BBs bIn to test this part of the script (don’t forget to connect the Mouse Displacement BB to Start). Notice that the orientation of the frame is pretty smooth, although it still rotates over its z-axis. We’ll stabilize this in the next step.
• Insert an Identity BB and a Set Orientation BB. Create a paramOp (<Vector> Get Dir <3D Entity <None>) above the Identity BB. Create a This parameter (Alt+T) above the paramOp and connect it to to 3D Entity input. Connect the output. Change the pIn type of the Identity BB to Vector and connect it to the paramOps output. Create a local vector parameter (name: direction_frame_orientation) and connect it to the pOut of the Identity BB. This gets the direction (that you just set using euler angles) of the frame and outputs it as a vector. • Create a shortcut to this parameter above the Set Orientation BB and insert it in the Dir pIn. The referential for this new orientation is None (the world), so you don’t have to edit any parameters. Remove the old loop and create a new one from the Set Orientation BBs bOut to the Get Mouse Displacement BBs bIn. Test your composition. | ![]() |
||||||
|
4
Constraining The Orientation You may have noticed that the orientation of the frame behaves poorly when you look completely down or up. If we were to attach a camera to this frame now, we would lose our sense of direction very quick! Furthermore, we don’t want to be able to look straight up or down (nobody is that athletic!). So we have to create some sort of constraint for the orientation of the direction_frame over the x-axis (looking up/down). For this we need the y component of the direction_frame_orientation vector (confused yet?). If your run the composition you will notice that this second component changes from +1 to -1 when you look completely up to down. Then this value reaches +0.9 or -0.9 we are going to check if you are still “displacing” in the “wrong” direction, the direction that you want to block If this is true we’ll multiply the value we input into the Set Component BBs Component 1 (controlling the rotation over the x-axis) with 0 (making the result 0 as well). When the value is between the limits, or you are “displacing” within the limits, we will set this multiplier back to 1.
• Ok, now let’s try this for real. Insert two Binary Switch BBs and a Parameter Selector BB somewhere underneath the Mouse Displacement BB. For the first condition, create three paramOps: • For the second condition, repeat this but change Superior to Inferior and vice-versa. Also change the -0.9 value into +0.9. |
||||||
| 5
Constraining The Orientation Continued Now we need to implement this BG between the Mouse Displacement BB and the Set Component BB. This shouldn’t be any problem.
• Insert a paramOp <Float> Multiplication <Float> Float> between the left multiplication paramOp and the Set Component BB. Insert this first paramOps pOut as the left pIn of this new one and a shortcut to the threshold parameter (inside the threshold BG) as the right one. Although we might return to this script later on, why not drag a BG around it (named ”orientation direction_frame”) to tidy things up. Give it a go. | ![]() |
||||||
![]() |
6 Position The Direction_frame This is an easy part. It makes sure the direction_frame goes everywhere the dummy_character goes. Underneath your newly created BG, insert a Set Position BB and loop it. Somewhere in the top part of your script, create a local 3D entity parameter, name it “dummy_character” and select the dummy character as the value. Now, whenever we need to refer to the dummy character we can use a shortcut to it, instead of using up a new parameter. Create a shortcut to it above the Set Position BB and insert it as the referential. Drag a new BG around it (“position direction_frame”), connect it and we’re done with this step! WARNING When using these kinds of shortcuts to parameters (objects, floats, vectors or whatever) you must realize that when you change the shortcut into another value (i.e. your script doesn’t work and you want to use another object as the referential), ALL shortcuts and the source change into this newly chosen referential! Most of the times you only want to change this one parameter, so you will have to disconnect it and create a new local parameter for a different object. This seems obvious and clear when you have but a few shortcuts, but it can cause a lot of debugging time when shortcuts are scattered through your whole composition. That is why for these parameters it’s best to keep the name and the value the same and both displayed in the source parameter (so you can quickly see if they’re not the same). |
||||||
| 7
Orientation Of The Dummy Character Since the real character follows the dummy character, we need to orientate the dummy character to the direction_frame to make our character look in the right direction. One tiny thing: we don’t want to use the up/down looking for the orientation of the dummy character. So we have to set the y value of the direction_frame_orientation vector to “0” before inserting it into the Set Orientation BB.
• Insert a Modify Component BB. Edit it’s settings (while selected, hit “S”). Check Modify Y. Create a shortcut to direction_frame_orientation and insert it as Vin (the first pIn). Make the Y pIn “0”. Create a local vector parameter underneath the Modify Component BB, name it “dummy_character_orientation” and connect it to the pOut. •Add a Set Orientation BB. Create a shortcut to “dummy_character” above it and set it as the Target of the BB (while selected, press “T” and connect the parameter). Connect the Modify Component BBs pOut to the Set Orientation BBs Dir pIn. Loop the whole thing and drag a new BG around it called “orientation dummy_character”.
| ![]() |
||||||
![]() |
8
Set The Real Character As we pointed out in the previous step, the real character (TheHammer_ROOT bodypart to be precise) is positioned on the dummy_character. We did this by creating a little temporary BG inside our animation script, attached to TheHammerAK (step 13 of the previous tutorial…).
• Go to this script, select the “temp” BG and cut it (Ctrl-X) and paste it (Ctrl-V) into the direction_frame Script. Let’s create another local 3D Entity parameter (name and value “TheHammer_ROOT) somewhere in the top part of your script and replace the old parameters in this temp BG. Replace the dummy_character parameters with shortcuts to the one we created earlier (Just to keep things tidy!). Rename the BG “set_character”, connect it to Start and give it a go. | ||||||
| 9 The Camera Now it’s time to create a camera (select the camera-icon from the main toolbar). The camera setup will open. Select the camera in the top left drop-down menu and press F2 to rename it to “thirdperson_camera”. Set all position and orientation data to “0”. Perspective must be checked. Also check that the Aspect Ratio is on and set to 4:3. Set the Focal Length to 30 (we want to see a little bit more of the environment). Set the Near Clip to 0.01 (everything closer will be clipped) and the Far Clip to 150. Setting the Near Clip a little but lower ensures that the character will be rendered when the camera is close to him or any other objects. When the player is on one side of the level, objects in the far distance (farther than 150) will be clipped. Later on we will use some fog to hide this. Clipping is what you can call the most basic of rendering-optimizations. On the left side of the setup, press Set IC.
• Back in the Schematic View, we will setup the position and orientation of the camera, with the direction frame as the referential. First, let’s create a local 3D Enitity parameter for our thirdperson_camera somewhere in the top part of our script. Now, copy-paste the whole “set character” BG. Rename it “set camera” (while closed, select it and press F2) and attach it to Start. Inside the BG, delete all the edited parameters and paste a shortcut to the thirdperson_camera parameter. Connect the thirdperson_camera to the Target parameter of both BBs. Create a This parameter (“Alt+T”) and connect it to the Referential Parameters of both BBs. Edit the Set Orientation BB. Notice that deleted Dir pIn is reset to (0,0,1). Now edit the Set Position BB and set the Position to (0.3, 0.8, -5) (a temporary value), Rename the Position parameter of the Set Position BB to “camera_position”. Now the camera will always be located slightly to the right, slightly above and quite a distance behind the direction frame. It will also copy the orientation of direction_frame. Try it (you might need to select the camera from the drop down menu above the viewport). We’ve saved our progress up to now as tutorial_part2a.CMO.
| ![]() |
||||||
![]() |
10
Camera Adjustments Although the movement of our camera already feels pretty good, we need to get closer to the action when we are looking up or down. The camera still goes through objects as well. To do this, we are going to change the distance from the direction_frame to the camera dynamically. In other words, we have two tasks: 1. If the y value of the orientation-vector becomes extreme, make the distance smaller. 2. If there is something moving in between our character and the camera, make the distance smaller.
• First, let’s get rid of that annoying 3D frame (if you haven’t done so already…). In the Level Manager, select the direction_frame and make it invisible by clicking on the eye-icon. Use the rightclick-menu to set the initial conditions again. Now let’s return to our direction_frame Script. Insert a Bezier Transform BB and an Interpolator BB. Change the type of the Interpolator BBs pOut to vector by double-clicking on it. • Now let’s create two local vector parameters:
| ||||||
| 11
Camera Orientation Adjustments Create a shortcut to “direction_frame_orientation” above the pIn of the Bezier Transform BBs. Insert it into the X pIn. A paramOp dialogue will appear, choose Get Y as the operation. Connect the pOut of the Bezier Transform BB to the Value pIn of the Interpolator BB. Now edit the Bezier Transform BB. The X value (the value you insert into the X pIn, in our case the Y value of the direction_frame_orientation!) can theoretically fluctuate between -1 and 1, so enter these values for the Xs. For this we want a value between 1 and 0 in return, the Ys.
• Now: if X goes near -1 or 1 (looking far up or down), the Y value must become 0, so our Interpolator will see to it that the camera will move closer to the character. If X goes near 0 (looking forward), the Y value must become 1, so our camera will slide back. To create smooth transitions create a curve as shown above. Connect the Bezier Transforms bOut to the Interpolator bIn, loop it back to the Bezier Transforms bIn, create a nice BG around it called “camera orientation_adjustments” and connect it all to Start. Note: you cannot test it yet because camera_position_temp is not connected to the Set Position BB in the “set camera” BG! | ![]() |
||||||
|
12
Camera Collision Adjustment For the collision adjustment we need another dummy object. From your Resource 3D Entities folder, drag “_dummy_camera” into your scene. It looks like a transparent red box. Back to the scripting: Position and orientate it in the same way you did for the camera, but now use the “dummy_character” as the referential for the position and the orientation. If you copied the entire BG, notice that you need to reset the Set Position BBs Position pIn to 0! Let’s call this BG “set _dummy_camera”. Test if this works (the box should follow your character around and copy it’s orientation). We are only going to test the collision when our character is moving or when he is looking around, otherwise the camera could change when you’re doing nothing.
| ||||||
13 Camera Collision Condition 1 So first we calculate if the camera needs to get closer because you are looking far up or down and we’ve put that vector in the camera_position_temp parameter. Then (in the same frame) we’ll test if the character is moving/orientating and if so, if the camera needs to go any closer because the dummy_camera is colliding with anything. Then we’ll take the temporary vector, adjust it and then we’ll put it in the “camera_position” parameter. • Insert a Binary Switch BB. This will test if the dummy_character is moving OR if the direction_frame changes its orientation. Add a paramOp <Boolean> Or <Boolean><Boolean> above the BB. To the left of that insert a paramOp <Boolean> Not equal <Vector><Vector>. • Now we have to go back to a script we’ve already made to create a parameter to test the translation. Go to the control script on the dummy_character (you can find it by looking in the Level Manager or by selecting Show All in the Schematic). Inside the BG called “translation” we need to create a parameter before the vector coming out of the Per Second BB enters the Translate BB. To do this, delete the connection, create a local vector parameter (“current_translation”) and insert it into the Translate BBs Vector pIn. Connect the Per Second BBs pOut to this parameter. • Now connect a shortcut to the new “current_translation” into the left pIn of the Not equal paramOp in the script we’re working on. Leave the right (0,0,0). Insert the result of the paramOp into the left pIn of the Or paramOp. |
![]() |
||||||
![]() |
14
Camera Collision Condition 2 Go to the BG “orientation direction_frame” and insert a Variation BB and a Parameter Selector BB behind the Set Orientation BB. Change the X pIn type (of the Variation BB) to Vector by double-clicking on it. Insert a shortcut (or the parameter itself, it’s pretty close) to “direction_frame_orientation” into the X pIn. Set the pOut type of the Parameter Selector BB to Boolean and connect a new local Boolean parameter underneath it called “orientating”. Edit the BB and set its pIns 0 to true and 1 to false.
• Now, if the direction_frame_orientation vector changes, the Change bOut will be activated. This needs to be connected to the Parameter Selector BBs bIn0, the No Change to bIn1. Don’t forget to connect the Variation BBs bIn to the Set Orientation BBs bOut. It’s not necessary to incorporate it into the whole loop; set up like this it runs each frame anyway. Now we can create a shortcut to “orientating” above the right pIn of the Or paramOp in the lower part of our script (see screen in previous step). Connect the result of that one into the Condition pIn of the Binary Switch BB. Loop the False bOut back into its bIn and connect the True bOut to a new Collision Detection BB. • In this new Collision Detection BB, set a shortcut to the dummy_camera as the Target parameter for this BB (“T” while the BB is selected). In the Level Manager we can already add a Fixed Obstacle Attribute (Found in the Collision Manager Category) to all objects you want the camera to avoid (exclude the dummy objects I suppose). Edit the parameter of the BB to change the Geometry Precision to “Faces”. In the settings (“S”) of the Collision Detection BB you can uncheck all, except if you want to create a local 3D Entity parameter to check which Obstacle is hit for debugging purposes (recommended). | ||||||
| 15
Collision Or Not Now that the conditions are in place, we can concentrate on what comes next. If the collision occurs, we need to transform the value of the “camera_position_temp” into the “camera_position_close” and insert it into the definite “camera_position”. Drop an Interpolator BB into your script but leave a lot space between it and the Collision Detection BB. Because, if the collision doesn’t occur the interpolation needs to be undone, in other words: the Value parameter of the Interpolator needs to transform back into “0”. For the transformation of the Value from 0 to 1 and vice-versa we are going to use a Bezier Transform BB. Insert it to the left of the Interpolator BB. The Bezier Transform BB has its own X Value pIn that needs changing, we’ll do that with a Parameter Selector BB (insert it to the right of the Collision Detection BB) and two “Addition” paramOps. The last BB we need is the Threshold BB to keep the X Value for the Bezier Transform between the 0 and 1. Insert it between the Parameter Selector BB and the Bezier Transform BB. Connect the BBs as shown in the screenshot; don’t forget to loop back from the Interpolator BB to the Binary Switch BBs bIn. • It’s time to connect the parameters properly. First, let’s work on the X Value of the Bezier Transform BB. Use a shortcut to it for both the paramOps left pIn (for the right pIns set the local values to 0.02 and -0.02) and connect them to the pIns of the Parameter Selector BB. Create another shortcut to X underneath the Parameter Selector BB and connect it to the pOut. • Paste another one above the Threshold BB and connect it to the first pIn AND to the pOut. Set the Min and Max pIns to 0 and 1. Create a shortcut to the Value pIn of the Interpolator BB underneath the Bezier Transform BB and connect it to the pOut of the Bezier Transform BB. Edit the BB to set a nice Bezier curve: start of slowly at (0,0), make a climb but make sure you reach the top before x=0.5. You can always tweak later on. • Now we have to paste the camera shortcuts (get them from inside the camera orientation_adjustments BG) for the Interpolator BB: camera_position_temp as the fist pIn, camera_position_close as the second, and camera_position as the pOut. Loop the bOut back to the first Binary Switch BB. Drag a nice BG around it (“camera collision_adjustments”) and connect it all to Start. Before you test it you might want to check the Show All Cameras setting for the Player Mode (you can find it in the main Preferences). Now you can see what happens to your camera when you select perspective view instead of camera view. Note: the speed of this adjustment is still framerate dependant (higher framerate means faster response). | ![]() |
||||||
![]() |
16
Strafing There are still some orientation problems we need to address: although our character can strafe, his own direction doesn’t change. This makes it look pretty silly. His upper body also doesn’t change when you are aiming up or down. For looking up and down, this means we have to change the orientation of the upper body. We will set a new orientation for our The Hammer Spine 1 bodypart that overrides the animation. For strafing it means we have to change the orientation of the whole character, without changing the movement direction of the dummy_character. For example, when we want to strafe left, the movement for the character is to the left and the TheHammer_Bip bodypart object needs to be pointing a bit to the left as well. Meanwhile, the The Hammer Spine 1 bodypart needs to be orientated to the right towards the way we are aiming. Confused? Wait until you see the orientation (pivots) of “The Hammer_Bip” and “The Hammer Spine 1”.
• You can view the pivots in the viewport by selecting the bodypart in the Levelmanager (don’t forget to unhide them temporarily). Note: All orientations of the characters are done on “The Hammer Bip” instead of “The Hammer_ROOT” because this root needs to stay orientated on the dummy_character.
|
||||||
17 Strafing Orientations We are going to need a couple of BBs: a Bezier Transform BB, a Threshold BB and two Set Euler Orientation BBs. Look up the x_translation parameter inside the dummy_character control script. Paste a shortcut to it above the Bezier Transform BB and enter it as the X pIn. For the strafing we are going to transform this value into an angle at which we are going to orientate “The Hammer Bip” and “The Hammer Spine 1”. Edit the parameters of the Bezier Transform BB so that the Xs run from -6 to 6 and the Ys from -0.9 to 0.9. Above the first Set Euler Orientation BB, paste a local 3D Entity parameter, set its value to “The Hammer_Bip” and make it the Target and the Referential parameter. Create another one for “The Hammer Spine 1” and set it as the Target and Referential for the second one. Edit the setting (“S”) for the Set Euler Orientation BBs (both of them) and uncheck Merge Angle Parameters. New pIns will appear allowing us to set each angle individually. Edit the BBs (double-click and press Ok) so that new parameters are automatically created for each pIn. • For “The Hammer Bip”: Rename the Y parameter of the Set Euler Orientation BB to “Y_bip”. Insert a “Multiplication” paramOp between it and the BB so that the Y_bip is multiplied by the float value “-1”). Paste a shortcut to “Y_bip” underneath the Bezier Transform and connect it to its pOut. • For “The Hammer Spine1”: Rename the X parameter of this Set Euler Orientation BB to “X_spine”, copy a shortcut to this underneath the Bezier Transform BB and connect this one to the pOut as well. Paste a shortcut to direction_frame_orientation vector into the X pIn of the Threshold BB. Specify a Get Y paramOp when it pops up. Set the Min of the Threshold BB to -0.8 and the Max to 0.7. Insert the pOut directly into the Y pIn of the Set Euler Orientation BB (of the spine!). Don’t forget to connect all the bOuts to the next BbIn and to loop the last bOut back to the Bezier Transform BB. Drag a nice BG around it and name it: “orientation bip and spine”. Run your composition. |
![]() |
||||||
![]() |
18
Priorities If you look closely to your final picture, you will notice some minor shaking or jittering when you orientate or move around. For example when you look far up and keep moving your mouse up you will notice some jittering. This is what you can call a priority issue. For example: you set the position for object A with object B as the referential, but somewhere else in your script, inside the same frame, you set the position for object B. Now it is possible that object A doesn’t have the exact position you want because you needed the “new” position of object B as the referential. This is especially annoying when you move the camera BEFORE you move your objects etc. Sometimes you don’t notice this, but for important objects such as your character and your camera-system it can be very distractive. At this point you can be glad you have all this stuff inside one script, because although you can edit the priority of individual building blocks, scripts and objects, it’s easier if you can just attach all the behavior graphs in an order that seems logical.
• First we have to reorder our BGs slightly. When this is done, we need to unloop all BBs inside our BGs and create one big loop through all BGs and back into the first one. You can do this by creating bOuts on your BGs. This way all our BBs inside our BGs are processed in the right order. Make sure that the loop is never broken inside a BG (you will notice soon enough!). That’s it. If you’ve followed all instructions carefully it just might work! If not: take the time to fix all errors because these first two tutorials are the most important ones of your game. |
||||||
End of part 2. |
![]() |
||||||