TorqueX/ListsTXB

From TDN

This page is a Work In Progress.


Contents

Introduction


This is an example component that demonstrates some the XML deserializer functions that are very useful if creating games in TXB. This example shows how you can create a list, that can be easily used in TXB. It also demonstrates creating components on the fly and adding them to objects.

This example assumes that you have explored the basic concepts about Torque X (TX) and Torque X Builder (TXB)

The Code

The code does most of the explaining of what each part does.

using System;
using System.Collections.Generic;

//Used for GarageGames' version of the deserializer
//[TorqueXmlDeserializeInclude]
using GarageGames.Torque.Core;

//Used by C# to tell it that the public property can be deserializer
//[XmlElement(ElementName = "MyListedExamples")]
using System.Xml.Serialization;

//used for the ReadOnlyArray
using GarageGames.Torque.Util;
using Microsoft.Xna.Framework;

//To allow use of interface IT2DForceGenerator
using GarageGames.Torque.Sim;
using GarageGames.Torque.T2D;

namespace GarageGames.Torque.PlatformerFramework
{
    [TorqueXmlSchemaType] //This is so it can be exported into the schema of components used by TXB
    class PodComponent : TorqueComponent
    {

        //[TorqueXmlDeserializeInclude]
        //tells the compiler that it's to be exported in the XML schema
        //The same effect can be achieved using a public varible, but it's
        //considered bad programming to declare public fields
        [TorqueXmlDeserializeInclude]

        //[XmlElement(ElementName = "Forces")]
        //Tells the complier the name of the property to be written to the XML file
        //If this is left out, the default name will be the varible name. In this case "rubble"
        [XmlElement(ElementName = "RubbleValues")]

        //Will store a list of rubble created in TXB
        private List<Rubble> rubble;

        protected override bool _OnRegister(TorqueObject owner)
        {
            if (rubble != null)
            {
                //loop through the "rubble" array
                foreach (Rubble singleObject in rubble)
                {
                    //Check to see if the object exists in the scene
                    //This should always be true since we have a nice list where we choose the object, but sometimes things get deleted!!
                    T2DSceneObject sceneObject = TorqueObjectDatabase.Instance.FindObject<T2DSceneObject>(singleObject.SceneObject.Name);
                    if (sceneObject != null)
                    {
                        //get a copy of the selected object that we will throw around
                        sceneObject = (T2DSceneObject)sceneObject.Clone();
                        sceneObject.Position = ((T2DSceneObject)Owner).Position;

                        //Add the component that created this T2DSceneObject!!
                        sceneObject.Components.AddComponent(singleObject);
                        
                        //Register the object with the database. Otherwise you won't find it!
                        TorqueObjectDatabase.Instance.Register(sceneObject);
                    }
                }
            }
            return base._OnRegister(owner);
        }
    }

    /// <summary>
    /// This is simply a container that holds various values
    /// </summary>
    public class Rubble : TorqueComponent, IT2DForceGenerator
    {
        //======================================================
        #region Private, protected, internal fields

        //The position where this object will stop
        private Vector2 finalPosition;
        //The time taken to get to distanation
        private float time;
        //The rate to rotate the object
        private float rotationSpeed;
        //The object that will be created and moved around
        private T2DSceneObject sceneObject;
        //Whether the finalPosition is relative to the Owner of the component
        private bool relative;
        //Stores the owner(TorqueObject) as (T2DSceneObject)
        T2DSceneObject myOwner;
        // force interface
        TorqueInterfaceWrap<IT2DForceGenerator> _forceInterface = new TorqueInterfaceWrap<IT2DForceGenerator>();
        //The distance from start to the final position
        float distance;
        //The starting position of the owner object
        Vector2 startPosition;

        #endregion

        //======================================================
        #region Public properties, operators, constants, and enums

        /// <summary>
        /// The time in which this component should reach the [finalPosition]
        /// </summary>
        [TorqueXmlSchemaType(DefaultValue = "10")]
        public float Time
        {
            get { return time; }
            set { time = value; }
        }

        /// <summary>
        /// The coordinates that this object should move to
        /// (DefaultValue = "5 5") gives the vector a value of Vector(5,5)
        /// </summary>
        [TorqueXmlSchemaType(DefaultValue = "25 25")]
        public Vector2 FinalPosition
        {
            get { return finalPosition; }
            set { finalPosition = value; }
        }

        /// <summary>
        /// The rate of rotation for the object
        /// </summary>
        [TorqueXmlSchemaType(DefaultValue = "10")]
        public float Rotation
        {
            get { return rotationSpeed; }
            set { rotationSpeed = value; }
        }

        /// <summary>
        /// The object(a TXB template) that will be created. In TXB this will show up as a 
        /// list of T2DSceneObjects that you have given names. T2DSceneObject names are given in the
        /// "Scripting" component, also it's recomended to mark them as "Template" by ticking the box.
        /// This lets you create as many as you wish(clones) to the object without desetroying it
        /// </summary>
        public T2DSceneObject SceneObject
        {
            get { return sceneObject; }
            set { sceneObject = value; }
        }

        /// <summary>
        /// Whether the FinalPosition of this object should be relative to
        /// the SceneObject that owns this component. A bool value in TXB
        /// looks like a tickbox. If the default value is "1" then it's ticked,
        /// "0" for unticked
        /// </summary>
        [TorqueXmlSchemaType(DefaultValue = "1")]
        public bool Relative
        {
            get { return relative; }
            set { relative = value; }
        }

        #endregion

        //======================================================
        #region Public methods

        /// <summary>
        /// IT2DForceGenerator interface method. Called by the Physics component before the current move is processed.
        /// </summary>
        public virtual void PreUpdateForces(Move move, float elapsed){}

        /// <summary>
        /// IT2DForceGenerator interface method. Called by the Physics component after the current move is processed.
        /// </summary>
        public virtual void PostUpdateForces(Move move, float elapsed)
        {
            if (Vector2.Distance(myOwner.Position, startPosition) > distance)
            {
                //This means we have arrived at our finalPosition!
                myOwner.Physics.Velocity = Vector2.Zero;
            }
        }

        /// <summary>
        /// Start the movement!
        /// </summary>
        public void Start()
        {
            //speed = distance(vector) / time
            myOwner.Physics.Velocity = (startPosition - FinalPosition) / time;
        }

        #endregion

        //======================================================
        #region Private, protected, internal methods

        protected override bool _OnRegister(TorqueObject owner)
        {
            //Find Owner as a T2DSceneObject
            myOwner = ((T2DSceneObject)owner);

            //myOwner.Physics is another Component which is by default added in 
            //every component, but you should check if it exists
            T2DPhysicsComponent physics = myOwner.Physics;
            Assert.Fatal(physics != null, "The component " + myOwner.Name + " requires a T2DPhysics component");
            //Add rotation to the Owner object
            physics.AngularVelocity = Rotation;
            //Store some varibles
            startPosition = myOwner.Position;
            if (Relative){
                finalPosition += myOwner.Position;
            }
            //calulate the distance form the start position to the finalPosition
            distance = Vector2.Distance(myOwner.Position, finalPosition);
            Start();

            return base._OnRegister(owner);
        }

        /// <summary>
        /// This method allows us to register iterfaces. IT2DForceGenerator is an example of an interface
        /// </summary>
        /// <param name="owner">Owner of the component</param>
        protected override void _RegisterInterfaces(TorqueObject owner)
        {
            // register the force generator interface to allow us to get
            // pre- and post- update physics callbacks
            Owner.RegisterCachedInterface("force", String.Empty, this, _forceInterface);
        }
        
        #endregion
    }
}

Importing the code in a game


Copy the entire code sample into you game. Compile it so that you generate the podComponent in the Schema. This will allow you to add the component onto T2DSceneObjects in TXB. Note that Rubble is not in TXB components list since we are missing the [TorqueXmlSchemaType] above the class definition.

The PodComponent

Now that you've successfully imported the component into TXB it's time to add it to an object. The object can be anything of your choice. This object will be the one that creates the rubble objects.

The Rubble Component

The podComponent also requires other T2DScenceObjects to be created to be attached to the podComponent. To do this create a component(s), they can be anything you like, making sure that they have the T2DPhysicsComponent. Give it them a name in the scripting tab.

Time to bring it together

Now that we have a T2DSceneObject set up with the podComponent installed and object(s) with a scripted name. We can now add Rubble to our podComponent. Fill in the details required and press the little green button. This will add those values to a RubbleComponent when the game is run. You can create multiple values of Rubble for the PodComponent. See the image.

Description
Enlarge
Description

Behind the scenes

When you save the scene, the values are written into the level data. When the scene loads it finds the PodComponent and its list of Rubble. Then looks at the T2DScenceObject that the rubble is referencing creates a template of it and then adds the Rubble component to it.

Conclusion

I hope this gives a little more insight into how the XML schema is created and methods to create lists types in TXB.
Please feel free to edit anything that you think could be more clearly explained.