Ruby Binding for TGEA
by Matthias Georgi · 01/23/2008 (6:04 pm) · 8 comments
After finishing a working implementation of my binding for the Ruby language, I'm gonna try now a tighter support using gccxml. After reading the post of Prairie Games and their work on the Python binding I was inspired to use the same route for generating wrappers for C++ classes.
My current implementation asks the Torque Scripting Environment for all defined classes, methods and fields and generates wrappers as script files, which call my binding code to interface with the Torque Engine. This has the drawback, that arguments and return values are converted to strings and my binding has to figure out, how to convert back and forth using the intermediate string representation.
This works quite well and has some kind of type safety in contrast to the Torque Scripting Language. I was able to convert all torque scripts to ruby scripts and the syntax is very nice to work with. Especially the scripts for the world editor got a lot clearer by using Ruby. A simple example will show you the advantages of using a full fledged scripting language. This is my implementation of the FileDialog GUI:
Essential was the implementation of the Event/Observer pattern, which simplifies the use of callbacks like onAdd. In Torque Script you are limited to one callback method for an event like onAdd. In my implementation you can attach more than one event listener to one instance or to the class itself. So you don't have to use namespaces, instead you attach the event listener to a particular instance, which should be done in the onAdd event handler, which gets called right after the creation of an object.
You may have noticed the child_accessor method. This one generates a accessor method for getting the child or descendant of a SimObject. This is cool, as you can avoid messing up the global namespace. You just use the internalName property of a child object and reach it via the accessor method.
So back to the GCCXML stuff. This is really exciting, as the xml format exposes the internal structure of a c++ program. I'm currently working on a small library, which parses the xml and makes the program structure accessible to the ruby world.
A simple Rakefile (like Make in Ruby) will transform the C++ classes to their xml representation and automatically generate wrapper code, which can be compiled by good old Visual Studio. This route allows a tight ruby binding avoiding the intermediate string representation. Furthermore all desired elements of the engine may be exposed to scripting and the burden of writing repetitive wrapper code is avoided.
If anyone is interested in ruby development, just let me know. I have invested a lot of time into this stuff (about 2 years) and like to share my experiences.
My current implementation asks the Torque Scripting Environment for all defined classes, methods and fields and generates wrappers as script files, which call my binding code to interface with the Torque Engine. This has the drawback, that arguments and return values are converted to strings and my binding has to figure out, how to convert back and forth using the intermediate string representation.
This works quite well and has some kind of type safety in contrast to the Torque Scripting Language. I was able to convert all torque scripts to ruby scripts and the syntax is very nice to work with. Especially the scripts for the world editor got a lot clearer by using Ruby. A simple example will show you the advantages of using a full fledged scripting language. This is my implementation of the FileDialog GUI:
class FileDialogClass < GuiControl
child_accessor :window, :okButton, :cancelButton, :dirTree, :textEdit, :fileList
def_on(:add) do
window.on(:close) do
close
end
okButton.on(:action) do |sender|
@callback.call selectedFile
close
end
cancelButton.on(:action) do |sender|
close
end
dirTree.on(:selectPath) do |sender, path|
fileList.setPath(path, @filespec)
end
fileList.on(:select) do |sender, id, filename|
textEdit.setText(filename)
end
textEdit.on(:return) do |sender|
@callback.call(selectedFile)
close
end
end
def open(title = "Open File", button_text = "Open", filespec = "*.*", &proc)
window.text = title
okButton.text = button_text
@filespec = filespec
@callback = proc
Canvas.pushDialog(self, 99)
fileList.setPath(dirTree.getSelectedPath, @filespec)
end
def selectedPath=(file)
dirTree.setSelectedPath file
end
def selectedPath
dirTree.getSelectedPath
end
def selectedFile
"#{selectedPath}/#{textEdit.getValue}"
end
def close
Canvas.popDialog(self)
end
endEssential was the implementation of the Event/Observer pattern, which simplifies the use of callbacks like onAdd. In Torque Script you are limited to one callback method for an event like onAdd. In my implementation you can attach more than one event listener to one instance or to the class itself. So you don't have to use namespaces, instead you attach the event listener to a particular instance, which should be done in the onAdd event handler, which gets called right after the creation of an object.
You may have noticed the child_accessor method. This one generates a accessor method for getting the child or descendant of a SimObject. This is cool, as you can avoid messing up the global namespace. You just use the internalName property of a child object and reach it via the accessor method.
So back to the GCCXML stuff. This is really exciting, as the xml format exposes the internal structure of a c++ program. I'm currently working on a small library, which parses the xml and makes the program structure accessible to the ruby world.
A simple Rakefile (like Make in Ruby) will transform the C++ classes to their xml representation and automatically generate wrapper code, which can be compiled by good old Visual Studio. This route allows a tight ruby binding avoiding the intermediate string representation. Furthermore all desired elements of the engine may be exposed to scripting and the burden of writing repetitive wrapper code is avoided.
If anyone is interested in ruby development, just let me know. I have invested a lot of time into this stuff (about 2 years) and like to share my experiences.
#2
any chance this will work with TGE (and on non Win systems)? :)
01/23/2008 (10:14 pm)
truly amazing.any chance this will work with TGE (and on non Win systems)? :)
#3
@Montgomery:
TGE should work fine, but It would require some merging of C++ code and removing some shader, material scripts.
I patched the engine on several places. First there were some little bugs in the engine code like wrong argument count for callbacks. A change was made in the GameConnection to avoid the ugly serverCmd* functions. Now the GameConnection itself dispatches the commmands and delegates to the server module, which in turn delegates to the corresponding module. Another change was made to the ActionMap class to issue the mouse/key callbacks to the ActionMap object itself to avoid global functions.
For non-win systems to work, ruby needs to be compiled, so you can distribute your own ruby executable. The engine must be compiled as dll extension, so a little change to the makefile is needed.
Porting and maintaining should be easier, once the componentized engine is available, as the code base is the same for TGEA and TGE.
01/24/2008 (3:42 am)
Thanks.@Montgomery:
TGE should work fine, but It would require some merging of C++ code and removing some shader, material scripts.
I patched the engine on several places. First there were some little bugs in the engine code like wrong argument count for callbacks. A change was made in the GameConnection to avoid the ugly serverCmd* functions. Now the GameConnection itself dispatches the commmands and delegates to the server module, which in turn delegates to the corresponding module. Another change was made to the ActionMap class to issue the mouse/key callbacks to the ActionMap object itself to avoid global functions.
For non-win systems to work, ruby needs to be compiled, so you can distribute your own ruby executable. The engine must be compiled as dll extension, so a little change to the makefile is needed.
Porting and maintaining should be easier, once the componentized engine is available, as the code base is the same for TGEA and TGE.
#4
01/24/2008 (9:33 am)
Cool stuff!
#5
01/24/2008 (11:52 am)
That is just cool! Any chance of a ruby binding for TGB? ;)
#6
I don't own the TGB engine, but as far as I know, TGB is based on the same scripting engine. So it would be possible, but would require rewriting of some scripts.
01/24/2008 (3:44 pm)
@Saiko:I don't own the TGB engine, but as far as I know, TGB is based on the same scripting engine. So it would be possible, but would require rewriting of some scripts.
#7
05/13/2008 (12:12 pm)
This is really exciting stuff Matthias! I've been away from game dev for awhile and working mostly with Ruby so it's great to see that as I jump back into the Torque experience I can leverage what I've learned. I would love to talk Ruby and Torque with you if you're interested.
#8
06/02/2008 (1:24 pm)
Hi Brandon, just write me an email. Your email seems to be outdated. 
Torque 3D Owner Novack
CyberianSoftware