Torque 2D game with C++
by Suigura · in Torque 2D Beginner · 02/07/2013 (10:50 am) · 15 replies
Hi,
my intention is to create a game, written in C++, based on the Torque 2D engine from Github.
Since Torque 2D was released as open source software I wanted to try the capabilities of Torque 2D.
From my first look over the codebase it seems like the only possibility to create games is "Torque Script", a scripting language which seems quite usefull for *most* users.
However for me the script language is irrelevant, as I have an indispensable set of tools that require a C++ codebase.
My question therefore: Is the Torque 2D engine the right choice for C++ game programmers?
my intention is to create a game, written in C++, based on the Torque 2D engine from Github.
Since Torque 2D was released as open source software I wanted to try the capabilities of Torque 2D.
From my first look over the codebase it seems like the only possibility to create games is "Torque Script", a scripting language which seems quite usefull for *most* users.
However for me the script language is irrelevant, as I have an indispensable set of tools that require a C++ codebase.
My question therefore: Is the Torque 2D engine the right choice for C++ game programmers?
#2
02/07/2013 (12:06 pm)
If you're a competent programmer you should be able to integrate your tools with the C++ engine code. Torque 2D is written in C++. I find your question very strange.
#3
02/07/2013 (12:35 pm)
Remember you get all the Torque2D source code and that's in C++
#4
1. Maintain a simple main.cs entry point script. Instead of loading up modules, you basically just want it to create the very base setup you need to run.
2. Create your objects entirely in C++. You will need to manually add them to a list for cleanup, since T2D is unmanaged.
3. Find an alternative to functions that use Con::executef, if you need callbacks.
You really are not limited since the full source is available. With the exception of the main.cs, you do not have to use TS at all.
02/07/2013 (12:57 pm)
@Suigura - As others have stated, you can write your entire game in C++ if you wish. With the current version of the engine, you will need to do the following:1. Maintain a simple main.cs entry point script. Instead of loading up modules, you basically just want it to create the very base setup you need to run.
2. Create your objects entirely in C++. You will need to manually add them to a list for cleanup, since T2D is unmanaged.
3. Find an alternative to functions that use Con::executef, if you need callbacks.
You really are not limited since the full source is available. With the exception of the main.cs, you do not have to use TS at all.
#5
Should the key codes be used to map function keys, like 112 for "F1"?
@Michael: Unfortunately integrating the engine code is not enough, so this means I have to provide the ABI compatibility code by myself?
You say it isn't possible to enter the event loop from C++ and a main.cs file is necessary for that, or do I misunderstand you?
02/07/2013 (1:27 pm)
@Lukas: Yes I've seen that, some things look a bit obscure like actionMap.Should the key codes be used to map function keys, like 112 for "F1"?
@Michael: Unfortunately integrating the engine code is not enough, so this means I have to provide the ABI compatibility code by myself?
You say it isn't possible to enter the event loop from C++ and a main.cs file is necessary for that, or do I misunderstand you?
#6
02/07/2013 (2:35 pm)
The current source will actually look for a main.cs file, read its contents, then evaluate it. This is hard-coded in the engine, unfortunately. However, this can be changed. Many have done so before. What I'm getting at is there are a few key places in the engine that you need to short circuit if you want a 100% pure C++ driven game.
#7
(note I'm trying to avoid line numbers which will probably get out of date on this open-source code-base quickly)
If you were to omit all this code from the rest of that method (or just stick in a "return") then Torque would not call the "main.cs" file.
Hooking into the engine primarily involves two things:
- Timing events
- Callbacks
The "defaultGame" is just an implementation of the "gameInterface" (in the same folder) and there you can see virtual methods like "mainLoop". The crudest method is to implement a new type and derive from "defaultGame" and just "hook" into the main-loop (or other) depending on what you need to do.
If you look further, you'll see that "defaultGame" is additionally derived from "Tickable". Have a look at this type, it's pretty self explanatory but it provides the time updates.
Any of your types (or maybe just one central one) can receive these time updates and perform work appropriately. Alternately you'll see in GameInterface "processEvent". Have a look at that code (again, it's pretty self explanatory) and you'll see that T2D has only a few standard events it uses but you are free to create any you want for whatever purpose you desire whether that be time-updates, communicating with your engine/editor etc.
All the above is in one place so you don't need to make a whole bunch of changes in lots of different places. Nothing in the engine knows about this part of the engine. It's either driven by time-updates directly from a Tickable (or a parent that's Tickable) or its called via a TorqueScript binding i.e. the "user" has called it.
All of the 2D stuff (including the asset and module systems) I wrote are callable directly from C++ i.e. you don't need to call it via TorqueScript. I even tried to keep the names identical where appropriate. The TorqueScript language bindings can only call public accessible stuff anyway so if it can be done in script, it'll be public to your code as well.
The only area where you might encounter an issue would be callbacks specifically designed for the scripts but really, there are not many of them and certainly not important ones. The thing is, even here it's possible to get hold of those callbacks pretty easily.
Your primary problem will be your ability to understand how the inside of the engine works. If you can do that then really, you've not got many changes to make.
You will have to deal with the engine in some areas on a scripting level though. For instance it has a few script variables that it uses but they are all optional and have defaults. Setting those in C++ is trivial however.
Hope this helps.
02/08/2013 (12:33 am)
You can easily stop the "main.cs" from executing if you so desire. Go to the "enginegamedefaultGame.cc", go to the method "initializeGame()" and look at the code following the line:const char* defaultScriptName = "main.cs";
(note I'm trying to avoid line numbers which will probably get out of date on this open-source code-base quickly)
If you were to omit all this code from the rest of that method (or just stick in a "return") then Torque would not call the "main.cs" file.
Hooking into the engine primarily involves two things:
- Timing events
- Callbacks
The "defaultGame" is just an implementation of the "gameInterface" (in the same folder) and there you can see virtual methods like "mainLoop". The crudest method is to implement a new type and derive from "defaultGame" and just "hook" into the main-loop (or other) depending on what you need to do.
If you look further, you'll see that "defaultGame" is additionally derived from "Tickable". Have a look at this type, it's pretty self explanatory but it provides the time updates.
Any of your types (or maybe just one central one) can receive these time updates and perform work appropriately. Alternately you'll see in GameInterface "processEvent". Have a look at that code (again, it's pretty self explanatory) and you'll see that T2D has only a few standard events it uses but you are free to create any you want for whatever purpose you desire whether that be time-updates, communicating with your engine/editor etc.
All the above is in one place so you don't need to make a whole bunch of changes in lots of different places. Nothing in the engine knows about this part of the engine. It's either driven by time-updates directly from a Tickable (or a parent that's Tickable) or its called via a TorqueScript binding i.e. the "user" has called it.
All of the 2D stuff (including the asset and module systems) I wrote are callable directly from C++ i.e. you don't need to call it via TorqueScript. I even tried to keep the names identical where appropriate. The TorqueScript language bindings can only call public accessible stuff anyway so if it can be done in script, it'll be public to your code as well.
The only area where you might encounter an issue would be callbacks specifically designed for the scripts but really, there are not many of them and certainly not important ones. The thing is, even here it's possible to get hold of those callbacks pretty easily.
Your primary problem will be your ability to understand how the inside of the engine works. If you can do that then really, you've not got many changes to make.
You will have to deal with the engine in some areas on a scripting level though. For instance it has a few script variables that it uses but they are all optional and have defaults. Setting those in C++ is trivial however.
Hope this helps.
#8
yes it helps thanks.
The idea is to create a T2D engine library to link against from multiple projects.
At first my expectation of this engine was that it would be provided as a library.
To fit this purpose it is not necessary to change the engine itself, but to provide API and ABI stability (this might sound uncommon, in windows terminology it is referred as avoiding the "DLL-hell" or "dependency hell").
Adding binary interface compatibility overlays, opaque pointers (or more specifically d-pointers), will help to keep track with the future development without extensive maintenance.
Setting up a custom implementation of the gameInterface should be convenient enough for my purpose.
Some things are still unclear. Doxygen helps much to understand the engine structure, but not in this case.
For example this piece of torque script from the MoveToToy:
function MoveToToy::createBackground( %this )
{
// Create the sprite.
%object = new Sprite();
// Set the sprite as "static" so it is not affected by gravity.
%object.setBodyType( static );
// Always try to configure a scene-object prior to adding it to a scene for best performance.
// Set the position.
%object.Position = "0 0";
// Set the size.
%object.Size = "100 75";
// Set to the furthest background layer.
%object.SceneLayer = 31;
// Set an image.
%object.Image = "ToyAssets:highlightBackground";
// Set the blend color.
%object.BlendColor = Bisque;
// Add the sprite to the scene.
SandboxScene.add( %object );
}
A C++ equivalent could be something like this:
void MoveToToy::createBackground()
{
//create the background sprite
Sprite *bgSprite = new Sprite();
bgSprite->setBodyType(b2BodyType::b2_staticBody);
bgSprite->setPosition(0, 0);
bgSprite->setSize(100, 75);
bgSprite->setSceneLayer(31);
//this method is protected, the nearest match is in SpriteProxyBase
bgSprite->setImage(???);
bgSprite->setBlendColor(ColorF::ColorF("Bisque"));
//the scene is declared somewhere else, it is retrieved from a static method here
SandboxScene::getInstance()->addToScene(bgSprite);
}
It seems like the function "bool setImage (const char *pImageAssetId)" from SpriteProxyBase is unknown in Sprite?
Edit: SpriteBase is missing the "using SpriteProxyBase::setImage;" in the public section..
02/08/2013 (5:28 am)
Hi Melv,yes it helps thanks.
The idea is to create a T2D engine library to link against from multiple projects.
At first my expectation of this engine was that it would be provided as a library.
To fit this purpose it is not necessary to change the engine itself, but to provide API and ABI stability (this might sound uncommon, in windows terminology it is referred as avoiding the "DLL-hell" or "dependency hell").
Adding binary interface compatibility overlays, opaque pointers (or more specifically d-pointers), will help to keep track with the future development without extensive maintenance.
Setting up a custom implementation of the gameInterface should be convenient enough for my purpose.
Some things are still unclear. Doxygen helps much to understand the engine structure, but not in this case.
For example this piece of torque script from the MoveToToy:
function MoveToToy::createBackground( %this )
{
// Create the sprite.
%object = new Sprite();
// Set the sprite as "static" so it is not affected by gravity.
%object.setBodyType( static );
// Always try to configure a scene-object prior to adding it to a scene for best performance.
// Set the position.
%object.Position = "0 0";
// Set the size.
%object.Size = "100 75";
// Set to the furthest background layer.
%object.SceneLayer = 31;
// Set an image.
%object.Image = "ToyAssets:highlightBackground";
// Set the blend color.
%object.BlendColor = Bisque;
// Add the sprite to the scene.
SandboxScene.add( %object );
}
A C++ equivalent could be something like this:
void MoveToToy::createBackground()
{
//create the background sprite
Sprite *bgSprite = new Sprite();
bgSprite->setBodyType(b2BodyType::b2_staticBody);
bgSprite->setPosition(0, 0);
bgSprite->setSize(100, 75);
bgSprite->setSceneLayer(31);
//this method is protected, the nearest match is in SpriteProxyBase
bgSprite->setImage(???);
bgSprite->setBlendColor(ColorF::ColorF("Bisque"));
//the scene is declared somewhere else, it is retrieved from a static method here
SandboxScene::getInstance()->addToScene(bgSprite);
}
It seems like the function "bool setImage (const char *pImageAssetId)" from SpriteProxyBase is unknown in Sprite?
Edit: SpriteBase is missing the "using SpriteProxyBase::setImage;" in the public section..
#9
Also - setImage() is handled by the SpriteProxyBase class and should not require any further fiddling unless you're intending to disambiguate a reference where more than one parent class contains the desired method....
02/08/2013 (5:53 pm)
If you examine the Torque 3D engine you'll find that this is the approach used there - the engine is provided as a library and the main application loads it. Perhaps some comparison to that codebase (which is similar in many respects to Torque 2D) could shed some light on this.Also - setImage() is handled by the SpriteProxyBase class and should not require any further fiddling unless you're intending to disambiguate a reference where more than one parent class contains the desired method....
#10
thanks for the information, I will have a look on the T3D engine.
Yes I assume that this is the case, the declaration of setImage() in SpriteBase is hiding the disambiguation of setImage() from SpriteProxyBase.
02/09/2013 (3:32 am)
Hi Richard,thanks for the information, I will have a look on the T3D engine.
Yes I assume that this is the case, the declaration of setImage() in SpriteBase is hiding the disambiguation of setImage() from SpriteProxyBase.
#11
The trick to finding these is to look at the type you're trying to manipulate and as I have already stated, look at the TorqueScript bindings. For "Sprite" this is "Sprite_ScriptBinding.h" but as you've already determined, the base-class "SpriteBase" contains most of the stuff therefore look in "SpriteBase_ScriptBinding.h".
Here you'll find the console methods like "setImage", "setAnimation" etc. Set Image is this:
The "setImage" comes from SpriteProxyBase. You pass the asset-Id and it works auto-magically.
The trick in finding these is to look at the SimObject chain. Only SimObject can have script-bindings.
02/09/2013 (8:39 am)
For starters, why are you looking at a sandbox scene if you're doing this all in C++?The trick to finding these is to look at the type you're trying to manipulate and as I have already stated, look at the TorqueScript bindings. For "Sprite" this is "Sprite_ScriptBinding.h" but as you've already determined, the base-class "SpriteBase" contains most of the stuff therefore look in "SpriteBase_ScriptBinding.h".
Here you'll find the console methods like "setImage", "setAnimation" etc. Set Image is this:
ConsoleMethod(SpriteBase, setImage, bool, 3, 4, "(string imageAssetId, [int frame]) - Sets the sprite image and optionally frame.\n"
"@param imageAssetId The image asset Id to display\n"
"@param frame The frame of the image to display\n"
"@return Returns true on success.")
{
// Calculate Frame.
U32 frame = argc >= 4 ? dAtoi(argv[3]) : 0;
// Set image.
return static_cast<SpriteProxyBase*>(object)->setImage( argv[2], frame );
}The "setImage" comes from SpriteProxyBase. You pass the asset-Id and it works auto-magically.
The trick in finding these is to look at the SimObject chain. Only SimObject can have script-bindings.
#12
02/09/2013 (8:49 am)
Ah sorry, forgot to mention. I believe you might be referring to those static methods? If so, those are for Torques field system and act as setters/getters/write-checks for that system which is why I made them protected. Historically in Torque, folks made them public for no good reason.
#13
I added a "return" in "initializeGame()", avoiding the script parsing completely.
Defined this class:
The "mainInitialize()" implementation is a copy of
with this call at the end, the custom game
and this function does what is currently done in Torque Script regarding the Canvas creation
These are just C++ copies of the functions that are called in script by createCanvas() and
setScreenMode(), in canvas.cs
Next, create a SceneWindow instance.
10/23/2013 (1:38 am)
I am trying to do this, following Melv's instructions:Quote:
If you were to omit all this code from the rest of that method (or just stick in a "return")
I added a "return" in "initializeGame()", avoiding the script parsing completely.
Quote:
The crudest method is to implement a new type and derive from "defaultGame"
Defined this class:
class CaGame: public DefaultGame
{
public:
virtual bool mainInitialize( int argc, const char **argv );
private:
bool createGame();
bool createCanvas();
bool setScreenMode();
};The "mainInitialize()" implementation is a copy of
bool DefaultGame::mainInitialize
with this call at the end, the custom game
if ( ! this->createGame() )
return false;and this function does what is currently done in Torque Script regarding the Canvas creation
bool CaGame::createGame()
{
if ( ! this->createCanvas() )
return false;
if ( ! this->setScreenMode() )
return false;
return true;
}These are just C++ copies of the functions that are called in script by createCanvas() and
setScreenMode(), in canvas.cs
bool CaGame::createCanvas()
{
AssertISV(!Canvas, "CreateCanvas: canvas has already been instantiated");
Platform::initWindow(Point2I(MIN_RESOLUTION_X, MIN_RESOLUTION_Y), "CaGame");
if (!Video::getResolutionList())
return false;
// create the canvas, and add it to the manager
Canvas = new GuiCanvas();
Canvas->registerObject("Canvas"); // automatically adds to GuiGroup
return true;
}
bool CaGame::setScreenMode()
{
return( Video::setScreenMode(320, 480, 32, false));
}Next, create a SceneWindow instance.
#14
Regarding loading assets, I ended up executing Torque Script just to load assets, by setting the main entry script to do
where this module contains only asset definitions.
Regarding response to events, this is very dependent on objects defined in script...not sure about the best way.
One solution is to subclass "SceneWindow" with a custom method for
10/27/2013 (9:11 pm)
These are the functions that create a SceneWindow instance and a Scene.bool CaGame::createSceneWindow()
{
// Create the scene window.
sceneWindow = new SceneWindow();
sceneWindow->onAdd();
sceneWindow->setCameraSize(Vector2(320,480));
// Set Gui profile.
GuiDefaultProfile = new GuiControlProfile();
GuiDefaultProfile->onAdd();
sceneWindow->setControlProfile(GuiDefaultProfile);
// Place the sceneWindow on the Canvas, set the new content control
Canvas->setContentControl(sceneWindow);
return true;
}
bool CaGame::createScene()
{
Sprite *background;
scene = new Scene();
scene->onAdd();
sceneWindow->setScene(scene);
// Create the sprite.
background = new Sprite();
background->onAdd();
static_cast<ImageFrameProvider*>(background)->setImage("module_assets:set_01_bk_wood_iphone");
static_cast<Sprite*>(background)->setSize(Vector2(320,480));
// Add to scene
scene->addToScene(background);
return true;
}Regarding loading assets, I ended up executing Torque Script just to load assets, by setting the main entry script to do
ModuleDatabase.LoadExplicit( "module_assets" );
where this module contains only asset definitions.
Regarding response to events, this is very dependent on objects defined in script...not sure about the best way.
One solution is to subclass "SceneWindow" with a custom method for
void CaSceneWindow::onMouseDown( const GuiEvent& eve )
{
}
#15
Can anyone post some hints about the best way to implement the mouse events without subclassing "SceneWindow"?
10/28/2013 (3:17 pm)
hmm..not sure if subclassing "SceneWindow" is the way to go...Can anyone post some hints about the best way to implement the mouse events without subclassing "SceneWindow"?
Torque Owner Lukas Joergensen
WinterLeaf Entertainment
Not sure how to handle callbacks in C++ tho.