Skip to main content
ScriptFX         Easy automation and editing of complex in-game graphical effects using Lua

This is an attempt at some documentation of my ScriptFX system. Here are the major features:

This was released under the LGPL in 2007 April - see this forum post

  • Interfaces can be designed for any scripting language, or any other means of input. An interface for Lua is included.
  • Create and control particle systems, meshes, billboards, and other ScriptFX effects through commands including Move, Rotate, Scale, and Tint.
  • Nest effects to create complex effects with less effort.
  • Get and set varibles on effects from LUA, without having to define them in C++.
  • You can use anything legal in your scripting language of choice, the only catch being how your language and ScriptFX communicate.
  • Call a scripting function every frame to update the effect however you wish, creating unlimited possibility.
  • Cleanly halt and hide an effect, including allowing existing particles to die out naturally, through the FXStop command. Effects can then easily be resumed with FXResume.
  • An Age variable, automatically updated on every effect, accessable through scripting, allowing timed events.


Link to .zip file (~7 megs, includes everything you need, no Ogre required): http://www.paranoidstudios.com/ScriptFX.zip
Just download, extract, run, and have fun with the .lua file.
Note that I have no error checking in, so if you screw something up, it will crash. Just work slowly and if something crashes, go back through and comment out lines to see where you crashed (Lua comments start with , so comment)

Source Code Download
I have finally cleaned up the code for release, months later. Sorry! Click here to download: http://www.paranoidstudios.com/ScriptFXsrc.zip
Download it and have even more fun. This should, emphasis on should, include everything you need, other than Ogre, Lua and Luabind. Install those and run. The source includes the effects demo I created, so you can get to messing around in the code as soon as possible. If you have any questions, contact me at urthenrampage on AIM, or urthen at paranoidstudios.com. I'll try and help as much as I can. Documentation is sparse in places but all names make it fairly obvious what things do. Good luck! If you make any modifications/extensions, email them to me and I'll see if I can include them in. (Code released under GNU LGPL - Ignore internal License.txt until I update that)

Here are some very short descriptions of the functions and what they take:

  • Variables:
    • string GetVar(string name); returns a variable from the effect. Returns "" if the variable isnt found. The string can be converted with tonumber() if required.
    • void SetVar(string name, string value); sets a variable on the effect. Note only string values can be passed, use tostring() if you need to pass a number.

  • Subeffect Creation
    • FXAddNode(string name) creates a SubNode. The subnode is invisible, but you can attach other subeffects, including subnodes, to it. It can be rotated and scaled.
    • FXAddEffect(string name, string effect) attaches another effect as a subeffect to this one. This effect can be moved, rotated, and scaled like any other subeffect. It cannot be tinted.
    • FXAddParticles(string name, string script) attaches a particlesystem. Scaling the system does not scale the particles but will scale the emitters, due to ogre restrictions. Tinting will tint the particles.
    • FXAddBillboard(string name, string material) attaches a single billboard. Scaling only uses the first two values of the function to set the height and width. Rotating can only be set, rather than roll/pitch/yaw. As such only the first value of the rotation function is used. Tinting colors the billboard.
    • FXAddMesh(string name, string mesh) attaches a mesh. Scaling, rotating, moving work normally. Tinting does not work.

  • Subeffect Manipulation (all angular measurements in degrees)
    • FXMove(string name, float x, float y, float z) moves the node the subeffect is attached to. If it is a subnode, it will move everything attached to that subnode as well.
    • FXRotate(string name, float pitch, float yaw, float roll) rotates the node, or in the case of a billboard, the texture is rotated to the pitch value.
    • FXScale(string name, float x, float y, float z) scales the node, or in the case of a billboard, sets the width and hight to the x and y values.
    • FXTint(string name, float red, float green, float blue) tints a billboard or a particlesystem. It uses the standard 0-1 system as Ogre.
    • FXKill(string name) instantly destroys any effect. If a SubNode is destroyed, anything attached will be re-attached to the effects' root node.
    • FXStop(string name) neatly stops all particlesystem emitters, hides billboards and meshes, and calls a full pause on effects. It has no function on SubNodes.
    • FXResume(string name) will reverse the effects of FXStop
    • FXAttach(string nameAnchor, string nameAttached) will attach the nameAttached subeffect to the nameAnchor subnode. If nameAnchor is not a subnode, it will do nothing.


If you try and create a subeffect with a duplicate name as one inside the same effect, it will fail silently. If you try and manipulate a subeffect that does not exist, it will also fail silently. Dont try and attach anything to itself or one of it's children, Ogre will probably crash.

Here are some screenshots for the little cutscene I put together using only my effects system.
(Note, FPS is relatively low because I ran this in debug mode. I re-ran it in release mode and got an average around 300 FPS. So its not horrendously slow, just FYI.)

ScriptFX_1.jpg
ScriptFX_2.jpg
ScriptFX_3.jpg
ScriptFX_4.jpg

Actual C++ code required to implement the entire demo, other than the internal effects code:

(Upon Ogre initialization)

Copy to clipboard
mEffectManager = new ScriptFX::EffectManager(mSceneMgr, new ScriptFX::LuaInterface()); ScriptFX::Effect* created = mEffectManager->AddEffect("demo");

(Every frame)

Copy to clipboard
LuatestApp::getSingleton().getEffectManager()->doCycle(evt.timeSinceLastFrame);