Torque X is a component-driven framework, so nearly all problems can be solved with a custom component attached to something. In this case, we’ll create a new custom component, called T3DRtsCameraComponent and then attach it to a blank TorqueObject.
For the sake of this post, I’m defining the RTS camera as one that does not change elevation, maintains a fixed camera angle pointing, and moves forward/backward/left/right. There are a lot of RTS games that add a lot more, like zooming, panning, etc., but this is just a quick post to get you started.
To implement an RTS camera in Torque X, the best method is to create a custom 3D component. You can download the completed component from here. (remember to rename the file from .txt to .cs) To create the component from scratch, open your project in Visual Studio and then right-click your Game project within the Solution Explorer window and choose Add New Item. Choose the T3DComponent template and then set the new component’s name to T3DRtsCameraComponent.cs.
In the new class, change the inheritance to the following:
T3DRtsCameraComponent : T3DComponent, ITickObject
to
T3DRtsCameraComponent : T3DCameraComponent, ITickObject
This will inherit the bulk of the camera rendering code from the existing 3D camera component. Now, we just add the behaviors we want. Start with a few tweakable properties, using my new favorite auto-properties.
public float CameraAngleDegrees { get; set; }
public float CameraMoveSpeed { get; set; }
public int PlayerIndex { get; set; }
public InputMap CameraInputMap { get; set; }
public float CameraAngleDegrees { get; set; }
public float CameraMoveSpeed { get; set; }
public int PlayerIndex { get; set; }
public InputMap CameraInputMap { get; set; }
The only bummer to auto-properties is the lack of default initialization. This can be accomodated with a class constructor.
public T3DRtsCameraComponent()
{
CameraMoveSpeed = 2.5f;
CameraAngleDegrees = -45;
}
The _OnRegister() method will need to request tick processing callbacks, so add a call to the AddTickCallback() method. Also, we need to setup the InputMap to respond to keyboard processing, but that can go in another method.
protected override bool _OnRegister(TorqueObject owner)
{
if (!base._OnRegister(owner))
return false;
ProcessList.Instance.AddTickCallback(owner, this);
_SetupInputMap();
return true;
}
The _SetupInputMap() method specifies the key bindings to move the camera around. I’m just using the Up, Down, Left, Right keys to move the camera around. You can change the key mappings or even add a mapping to respond to the Xbox game controller.
protected virtual void _SetupInputMap()
{
if (PlayerManager.Instance.GetPlayer(PlayerIndex).ControlObject == null)
{
PlayerManager.Instance.GetPlayer(PlayerIndex).ControlObject = Owner;
CameraInputMap = PlayerManager.Instance.GetPlayer(PlayerIndex).InputMap;
}
else
{
CameraInputMap = new InputMap();
}
int keyboardId = InputManager.Instance.FindDevice("keyboard");
�
if (keyboardId >= 0)
{
CameraInputMap.BindMove(keyboardId, (int)Keys.Up, MoveMapTypes.StickDigitalUp, 0);
CameraInputMap.BindMove(keyboardId, (int)Keys.Down, MoveMapTypes.StickDigitalDown, 0);
CameraInputMap.BindMove(keyboardId, (int)Keys.Left, MoveMapTypes.StickDigitalLeft, 0);
CameraInputMap.BindMove(keyboardId, (int)Keys.Right, MoveMapTypes.StickDigitalRight, 0);
}
}
With the input map ready, it’s time for the real code. The important details happen in the _UpdateTransform() and ProcessTick() methods. Let’s start with the ProcessTick() method. In addition to receiving a tick callback every couple of milliseconds, this method also receives a Move structure. When we setup the input map, we associated the arrow keys with different MoveMapTypes. As result, anytime the arrow keys are pressed, the Move structure will capture those inputs. The bottom-line is that we can check the Move structure to see which arrows have been pressed – and it also works when the Xbox game controllers are connected. In the ProcessTick() method, we simply take the amount of X and Y pressed and then multiply that value by a Speed variable. The product is all dropped into a Vector3 object that increments the X,Y,Z position of the camera.
public void ProcessTick(Move move, float elapsed)
{
if (move == null)
return;
if (move.Sticks.Count > 0)
{
Vector3 moveDirection = new Vector3((move.Sticks[0].X * CameraMoveSpeed), (move.Sticks[0].Y * CameraMoveSpeed), 0);
SceneGroup.Position += moveDirection;
}
}
Next, the _UpdateTransform() is called by the scene graph when the transform of an object (such as our camera) is updated. We’ll use this method to set the downward angle of the camera object. Remember that you can use the public property to easily adjust the angle of the camera.
protected override void _UpdateTransform()
{
SceneGroup.Rotation = Quaternion.CreateFromYawPitchRoll(0, MathHelper.ToRadians(CameraAngleDegrees), 0);
_transform = SceneGroup.Transform;
}
Set the New Camera Component in the Level File
Now that we have a working RTS camera component, we need to change the level data file to use it. By default, each new Torque X 3D project creates a camera that uses the FreeCameraComponent. We need to change that file to use our new RTS camera. Open the levelData.txscene file and replace:
<CameraComponent type="GarageGames.Torque.T3D.FreeCameraComponent" name="CameraComponent" />
with
<CameraComponent type="StarterGame3D.T3DRtsCameraComponent" name="CameraComponent" />
If you need to get a reference to the camera component, say to move the camera around programatically, you can use the TorqueObjectDatabase to find the RTS camera component.
T3DRtsCameraComponent camera = TorqueObjectDatabase.Instance.FindObject<T3DRtsCameraComponent>("CameraComponent");
sceneView.Camera = camera;
Here’s how the RTS camera looks in action, combined with the water surface material I described in the last post.
Since the RTS camera is just a component, we can set the initial position and altitude in the levelData.txscene file. Just set the Position Z value that belongs to the T3DSceneComponent attached. There’s a lot more that can be done with this camera to be more full-featured, such as zomming, panning around, mapping the input map to the Xbox game controller, but this is a good starting point for adding such code.