-
Notifications
You must be signed in to change notification settings - Fork 2
Home
This is the T3DSharp Framework files designed to be included via CMake
- Install dotnet-sdk 7.0 (Chocolatey:
cinst dotnet-sdk
) - Download the source
- Make a new directory in the "My Projects" folder (name it as you please)
- Download https://github.com/lukaspj/T3DSharp and extract into "My Projects//game/data/T3DSharp"
- Download https://github.com/lukaspj/T3DSharp-CoinCollection and extract into "My Projects//game/data/CoinCollection"
- CMake Configure (set
TORQUE_ENTRY_FUNCTION
toCSharpEntryFunction
) - CMake Generate
The T3DSharp project will be added to the VS solution when you build the source and all of the C# code in the CoinCollection module is added to the T3DSharpGame
project.
Then a new solution is created in "My Projects/BaseGame/game/data/T3DSharp" with all the C# projects.
In this repo you can find three template C# projects:
- T3DSharpGame - The entrypoint for the game, runs Torque3D and loads the C# types
- T3DSharpFramework - An auto-generated C# layer to interface with T3D through the EngineAPI CInterface
- T3DSharpGenerator - A generator that can generate the ConsoleObject class hierarchy and other console object definitions
The C# project uses the Console to handle calls from the engine and EngineAPI for interacting with the engine.
It's kindda vague what the Console in Torque3D actually is. It's a type-system but it can also lookup instances of objects and it might do other stuff as well..
The crux of the matter is that the Console is one of the core components that makes TorqueScript work, and the C# implementation taps into this engine to communicate with the engine. Thus anything that is accessible in the console, should be accessible in C#.
No, the generator has already been run in this sample. You can find the result under Engine/lib/t3dsharp/T3DSharpFramework/Generated
. Feel free to run it again if you want to mess with it though (you might have to do a little bit of manual editing of the output atm. but it should be manageable).
A quick introduction to T3DSharp, the macros in the C++ code DefineEngineFunction
and the like also define a CInterface-based version of that function that can be called by consumers of Torque3D as a .dll
. A CInterface version of a function is a compiler-independent simple version of a C++ function based on the C-language. It is limited to C's features as opposed to C++, which means no operator overloading, no class-methods, no virtual methods etc. This significantly simplifies the interface between the C++ code and a potential scripting language.
The CInterface version of these functions are typed, so, as opposed to TorqueScript, C# doesn't have to convert everything to string before it can call the function. It can just call it with the actual parameters that function expects. However, this typing is a one-way street. The console is built up around TorqueScript's stringly-typing where everything is a string, so when the engine wants to call the script it converts everything to string before it sends the command.
TL:DR; almost all calls from C# to C++ is statically typed (variadic functions are an exception atm), calls from C++ to C# will be dynamically typed.
In order to compensate for the dynamic typing that happens when the engine calls a C# function, the T3DSharp layer analyses your C# code, and checks that the number of parameters is correct and converts arguments from string to whatever your function expects.
T3DSharp utilizes three annotations to simplify integration with the engine.
-
[ScriptEntryPoint]
- Used inProgram.cs
to specify the equivalent of themain.cs
TorqueScript file. -
[ConsoleFunction]
- Use on apublic static
function to register the function (similar to a TorqueScript function definition). The class name will be used as the namespace. For example, thepublic static void Foo()
in the classBar
can be called as bothBar::Foo()
andFoo()
-
[ConsoleClass]
- Used to specify that the class represents a class that should be accessible from the console. If you have a C# classBar
you can't writenew Bar()
in TorqueScript, but you can writenew ScriptObject() { Class ="Bar"; };
// Works mostly like in TorqueScript, except type-safe.
// Also you have to manually "register" the object as you would in C++.
var Foo = new SimObject("Foo") {
// You can set static properties here
CanSaveDynamicFields = true,
};
// Atm, you have to set dynamic properties outside of the initializer:
Foo.SetFieldValue("dynamicProperty", "bar");
Foo.RegisterObject();
new SimObject("Foo").RegisterObject();
// Find by name
Sim.FindObject<SimObject>("Foo");
// Find by ID
Sim.FindObject<SimObject>(1234);
// If the string can be converted to a number, it will be treated as an ID instead of a name
Sim.FindObject<SimObject>("1234");
// Since the C# objects are representations of the underlying C++,
// you might sometimes have an object instance of type A, but
// you know it should be of type B but C# won't let you cast from A to B.
// Then you can do an unsafe-cast from A to B.
new SimSet("Foo").RegisterObject();
var Foo = Sim.FindObject<SimObject>("Foo");
// Cast from SimObject to SimSet
var FooAsSimSet = Foo.As<SimSet>();
// You can use the helper class GenericMarshal to convert from/to string
GenericMarshal.StringTo<int>("123");
GenericMarshal.ToString(123);
GenericMarshal.StringTo<Point3F>("1 2 3");
GenericMarshal.ToString(new Point3F {X = 1.0f, Y = 2.0f, Z = 3.0f});
// Tagged strings is used extensively in TorqueScript for networking purposes.
// In TorqueScript, tagged strings are specified by using single quotes (').
// In C# you can use string extensions instead:
var taggedStr = "myString".Tag();
var str = taggedStr.DeTag();
// Colors in TorqueScript are supported by special escaped characters like \c0.
// In order to implement this similarly in C#, we use string extensions:
"\\c0SomeText".ColorEscape();
// All engine functions are bundled by their scope. Most exist in the "Global" scope.
// Except from the explicit scope, this looks quite like TorqueScript. The only
// caveat is that they are case-sensitive in C#.
Global.Echo("Some log message here");
// You can't call TorqueScript directly, but you can use the engine functions Call or Eval
string arg1 = "foo";
string arg2 = "bar";
Global.Call("someTorqueScriptFunction", foo, bar);
Global.Eval($"someTorqueScriptFunction({foo}, {bar});");
In the CoinCollection
C# module, you might see properties like this here and there:
public CoinCollectionGameConnection Client {
get => Sim.FindObject<CoinCollectionGameConnection>(GetFieldValue("client"));
set => SetFieldValue("client", GenericMarshal.ToString(value));
}
The purpose here is to formalize a dynamic property, making it type-safe and well-documented. This is quite boiler-plate looking, at some point a better solution should be found for this.