TorqueX/Basic3DTutorial
From TDN
|
[edit] Introduction
[edit] Getting StartedNote: Portions shamelessly stolen from the FPS demo, but greatly pared down and simplified to reduce clutter for newbies. The key to all this is to create a Controller component that acts like a traffic cop for what object is currently hooked up to the player's input and what camera is generating the output. Any object that takes input and has a camera for output will implement the IControllable interface. We'll have two:
2. TestCamera - a component that does a simple free-look camera, except with an input mapped to switch back to the player.
[edit] FreeLook CameraFirst, create and define a FreeLook test camera, both in the XML and in the code. You need to override FreeCameraComponent because your camera must implement IControllable, as well as adding buttons to the input map to switch back to the player's camera. Be sure to grab the ControlComponent.cs file from the player folder in the FPSDemo. XML Object (replace the existing one in the starter XML).
<TorqueObject type="GarageGames.Torque.Core.TorqueObject" name="Camera">
<Components>
<CameraComponent type="StarterGame3D.TestCamera" name="CameraComponent">
<FarDistance>3000</FarDistance>
<FOV>1.57</FOV>
</CameraComponent>
<SceneComponent type="GarageGames.Torque.T3D.T3DSceneComponent">
<Position>
<X>1024</X>
<Y>1024</Y>
<Z>300</Z>
</Position>
</SceneComponent>
</Components>
</TorqueObject>
using System;
using System.Collections.Generic;
using System.Text;
using GarageGames.Torque.T3D;
using GarageGames.Torque.Sim;
using GarageGames.Torque.Platform;
using Microsoft.Xna.Framework.Input;
namespace StarterGame3D
{
/// <summary>
/// A freelook test camera
/// </summary>
public class TestCamera : FreeCameraComponent, IControllable
{
//======================================================
#region Public properties
public ControllerComponent Controller
{
get { return _controller; }
set { _controller = value; }
}
#endregion
//======================================================
#region Public methods
/// <summary>
/// Returns the input map used by the specified player.
/// </summary>
/// <param name="playerIndex">The player index of this camera.</param>
/// <returns>The input map used by this camera.</returns>
public InputMap GetInputMap(int playerIndex)
{
if (playerIndex != PlayerIndex)
return null;
return InputMap;
}
protected void ToggleControlObject(float val)
{
if (val > 0.0f)
_controller.UseBaseControlObject();
}
public void OnControlGained(int playerIndex)
{
Game.Instance.SceneView.Camera = this;
}
public void OnControlLost() { }
#endregion
//======================================================
#region Private, protected, internal methods
protected override void _SetupInputMap()
{
base._SetupInputMap();
int gamepadId = InputManager.Instance.FindDevice("gamepad" + PlayerIndex);
if (gamepadId >= 0)
{
InputMap.BindAction(gamepadId, (int)XGamePadDevice.GamePadObjects.Y, ToggleControlObject);
}
int keyboardId = InputManager.Instance.FindDevice("keyboard");
if (keyboardId >= 0)
InputMap.BindAction(keyboardId, (int)Keys.Y, ToggleControlObject);
}
#endregion
//======================================================
#region Private, protected, internal fields
ControllerComponent _controller;
#endregion
}
}
[edit] PlayerOn your player (note, some components omitted), make sure you have a Controller component, a PlayerComponent, and a CameraComponent. We'll also rely on the default physics implementation to move us forward/backward, etc (see below for that XML).
<Player type="GarageGames.Torque.Core.TorqueObject" name="PlayerTemplate">
<IsTemplate>true</IsTemplate>
<ObjectType>
<object objTypeRef="Player" />
</ObjectType>
<Components>
<ControllerComponent type="StarterGame3D.ControllerComponent" />
<PlayerComponent type="StarterGame3D.PlayerComponent">
<SceneGroupName>PlayerMesh</SceneGroupName>
<PlayerIndex>1</PlayerIndex>
</PlayerComponent>
<CameraComponent type="StarterGame3D.PlayerCameraComponent">
<SceneGroupName>PlayerMesh</SceneGroupName>
<TransformInterfaceName>cam</TransformInterfaceName>
<CameraSpeed>50</CameraSpeed>
<FarDistance>1000</FarDistance>
<FOV>1.57</FOV>
<BoundaryObjectTypes>
<object objTypeRef="Terrain" />
<object objTypeRef="StaticGeometry" />
</BoundaryObjectTypes>
<RigidManager nameRef="RigidManager" />
<PositionOffset>
<X>0.0</X>
<Y>-9.0</Y>
<Z>1</Z>
</PositionOffset>
<RotationOffset>
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</RotationOffset>
</CameraComponent>
</Components>
</Player>
[edit] ControllerGet the IControllable interface and ControllerComponent from the FPS starter demo, I used them essentially unchanged. They're in the player folder.
[edit] Player CameraPlayerCameraComponent. Notice that I am exposing an Update() method that will make the camera update its position. The FPS demo camera seems to work as a side-effect of the fact that the view angle is constantly being updated, which causes T3DCameraComponent to set the TransformDirty flag and recalculate the transform matrix. But in my attempts I wasn't doing that since I don't have a view angle, so my camera was just stationary in the world and didn't appear to be mounted to the vehicle. I just assumed that any T3DCameraComponent derived object would automatically track its associated owner, similar to the way the control/physics automatically deals with moving, but it doesn't. PlayerCameraComponent.cs
using System;
using System.Collections.Generic;
using System.Text;
using GarageGames.Torque.Core;
using GarageGames.Torque.T3D;
using Microsoft.Xna.Framework;
namespace StarterGame3D
{
/// <summary>
/// Third person camera
/// </summary>
public class PlayerCameraComponent : T3DCameraComponent
{
//======================================================
#region Public methods
public override void CopyTo(TorqueComponent obj)
{
base.CopyTo(obj);
PlayerCameraComponent obj2 = (PlayerCameraComponent)obj;
}
public void Update()
{
//we want to trigger a dirty flag so the matrix will get recalculated
this.RotatedPositionOffset = RotatedPositionOffset + Vector3.Zero;
}
#endregion
//======================================================
#region Private, protected, internal methods
protected override bool _OnRegister(TorqueObject owner)
{
if (!base._OnRegister(owner))
return false;
return true;
}
protected override void _UpdateTransform()
{
base._UpdateTransform();
}
#endregion
}
}
[edit] PlayerComponentNow the PlayerComponent, which sets up the input map. The ControlComponent will automatically handle moving around on the ground (see below). PlayerComponent.cs
using System;
using System.Collections.Generic;
using System.Text;
using GarageGames.Torque.T3D;
using GarageGames.Torque.Core;
using GarageGames.Torque.Sim;
using GarageGames.Torque.Platform;
using Microsoft.Xna.Framework.Input;
using GarageGames.Torque.GUI;
namespace StarterGame3D
{
public class PlayerComponent : T3DInputComponent, IControllable
{
//======================================================
#region Public properties, operators, constants, and enums
public TestCamera FreeCamera
{
get { return _freeCamera; }
set { _freeCamera = value; }
}
#endregion
//======================================================
#region Public methods
public override void CopyTo(TorqueComponent obj)
{
base.CopyTo(obj);
//TODO: make sure to copy properties here
(obj as PlayerComponent).PlayerIndex = this.PlayerIndex;
}
#endregion
//======================================================
#region Private, protected, internal methods
protected override bool _OnRegister(TorqueObject owner)
{
if (!base._OnRegister(owner))
return false;
return true;
}
protected override void _PostRegister()
{
base._PostRegister();
_controllerComponent = Owner.Components.FindComponent<ControllerComponent>();
}
protected override void _RegisterInterfaces(TorqueObject owner)
{
base._RegisterInterfaces(owner);
// todo: register interfaces to be accessed by other components
// E.g.,
// Owner.RegisterCachedInterface("float", "interface name", this, _ourInterface);
}
protected override void _SetupInput(InputMap inputMap, int gamepad, int keyboard)
{
// move
inputMap.BindMove(gamepad, (int)XGamePadDevice.GamePadObjects.LeftThumbX
, MoveMapTypes.StickAnalogHorizontal, 0);
inputMap.BindMove(gamepad, (int)XGamePadDevice.GamePadObjects.LeftThumbY
, MoveMapTypes.StickAnalogVertical, 0);
// look
inputMap.BindMove(gamepad, (int)XGamePadDevice.GamePadObjects.RightThumbX
, MoveMapTypes.StickAnalogHorizontal, 1);
inputMap.BindMove(gamepad, (int)XGamePadDevice.GamePadObjects.RightThumbY
, MoveMapTypes.StickAnalogVertical, 1);
inputMap.BindAction(gamepad, (int)XGamePadDevice.GamePadObjects.Y, ToggleCamera);
// do keyboard
{
// wasd
inputMap.BindMove(keyboard, (int)Keys.D, MoveMapTypes.StickDigitalRight, 0);
inputMap.BindMove(keyboard, (int)Keys.A, MoveMapTypes.StickDigitalLeft, 0);
inputMap.BindMove(keyboard, (int)Keys.W, MoveMapTypes.StickDigitalUp, 0);
inputMap.BindMove(keyboard, (int)Keys.S, MoveMapTypes.StickDigitalDown, 0);
// arrows
inputMap.BindMove(keyboard, (int)Keys.Right, MoveMapTypes.StickDigitalRight, 1);
inputMap.BindMove(keyboard, (int)Keys.Left, MoveMapTypes.StickDigitalLeft, 1);
inputMap.BindMove(keyboard, (int)Keys.Up, MoveMapTypes.StickDigitalUp, 1);
inputMap.BindMove(keyboard, (int)Keys.Down, MoveMapTypes.StickDigitalDown, 1);
inputMap.BindAction(keyboard, (int)Keys.Y, ToggleCamera);
}
}
protected void ToggleCamera(float val)
{
if (val < 1.0f)
return;
_controllerComponent.SetControlObject(_freeCamera as IControllable);
}
protected override void _UpdateInput(Move move, float dt)
{
PlayerCameraComponent cam = Owner.Components.FindComponent<PlayerCameraComponent>();
if (cam != null)
{
cam.Update();
}
}
#endregion
//======================================================
#region Private, protected, internal fields
private ControllerComponent _controllerComponent;
private TestCamera _freeCamera;
#endregion
#region IControllable Members
public ControllerComponent Controller
{
get
{
return null;
}
set
{
}
}
public InputMap GetInputMap(int playerIndex)
{
return _inputMap;
}
public void OnControlGained(int playerIndex)
{
PlayerCameraComponent camera = Owner.Components.FindComponent<PlayerCameraComponent>();
//use same world index or we'll get the aborted error on the clip map texture
camera.WorldViewIndex = FreeCamera.WorldViewIndex;
GUISceneview sceneView = Game.Instance.SceneView;
sceneView.Camera = camera;
}
public void OnControlLost()
{
}
#endregion
}
}
[edit] PhysicsNow the control/physics defined on the player in the XML. To change the physics of your player you would override the control component and states and define custom ones here in the XML that will map to those classes. Then those states can adjust things like movement speed, etc.
<ControlComponent type="GarageGames.Torque.T3D.T3DGroundControlComponent">
<SceneGroupName>PlayerMesh</SceneGroupName>
<ControlStates>
<ControlState>
<Name>OnGround</Name>
<Type>GarageGames.Torque.T3D.OnGroundControlState</Type>
</ControlState>
<ControlState>
<Name>InAir</Name>
<Type>GarageGames.Torque.T3D.InAirControlState</Type>
</ControlState>
</ControlStates>
<StartState>InAir</StartState>
</ControlComponent>
<RigidComponent type="GarageGames.Torque.T3D.T3DRigidComponent">
<SceneGroupName>PlayerMesh</SceneGroupName>
<RenderCollisionBounds>false</RenderCollisionBounds>
<ResolveCollisions>true</ResolveCollisions>
<CollisionShapes>
<CollisionShape>
<Shape type="GarageGames.Torque.T3D.RigidCollision.CollisionSphereShape">
<Radius>5.0</Radius>
<Center>
<X>0.0</X>
<Y>0.0</Y>
<Z>0.0</Z>
</Center>
</Shape>
</CollisionShape>
</CollisionShapes>
<RotationScale>0.0</RotationScale>
<GravityScale>1.5</GravityScale>
<RigidManager nameRef="RigidManager" />
<RigidMaterial type="GarageGames.Torque.T3D.RigidCollision.RigidMaterial">
<Restitution>0.0</Restitution>
</RigidMaterial>
</RigidComponent>
[edit] Game.BeginRunLastly, in BeginRun() we'll go ahead and kick off this game.
private GUISceneview _sceneView;
public GUISceneview SceneView
{
get { return _sceneView; }
}
protected override void BeginRun()
{
base.BeginRun();
// load our scene objects from XML.
SceneLoader.Load(@"data\levels\levelData.txscene");
//set sceneview
_sceneView = TorqueObjectDatabase.Instance.FindObject<GUISceneview>("DefaultSceneView");
//grab our free camera, we'll fake playerindex for now
TestCamera freeCamera = TorqueObjectDatabase.Instance.FindObject<TestCamera>("CameraComponent");
freeCamera.PlayerIndex = 1;
//get player template and clone it
TorqueObject template = TorqueObjectDatabase.Instance.FindObject<TorqueObject>("PlayerTemplate");
TorqueObject player = template.CloneT();
player.Name = "Player1";
//setup components on instance before we register it
ControllerComponent controller = player.Components.FindComponent<ControllerComponent>();
PlayerComponent playerComponent = player.Components.FindComponent<PlayerComponent>();
PlayerCameraComponent playerCamera = player.Components.FindComponent<PlayerCameraComponent>();
controller.BaseControlObject = playerComponent;
controller.PlayerIndex = 1;
playerComponent.PlayerIndex = 1;
//now register
if (!TorqueObjectDatabase.Instance.Register(player))
{
System.Diagnostics.Debug.WriteLine("Failed to register!");
}
//then setup control
controller.UseBaseControlObject();
//make sure gui is prepared
GUICanvas.Instance.OnPreRender();
//assign freecam to player
playerComponent.FreeCamera = freeCamera;
//now assign camera to start off with
_sceneView.Camera = playerCamera;
}
[edit] ConclusionI hope this has been useful in introducing you to TorqueX 3D. |



