Coordinated Effects
From Multiverse
| Client Scripting |
|
Overview • Coordinated Effects • Fireball Example • Terrain Decals • Compositors • About Shadows • Client Animation System • Creating Your Own Animations • Scripting Avatar Appearance |
| Reference |
Contents |
Introduction
A coordinated effect is a group of visual and sound effects that execute together as a result of a single specified event. A coordinated effect can include anything that the Multiverse Client is capable of producing, including animation, decals, particle systems, sounds, and projectiles.
In general, the client handles the details of complex visual effects, but the server performs gameplay-critical computations to prevent players from cheating with a hacked client. Thus, a client script creates a coordinated effect, but the client executes the script as a result of a message from the server. You use the Client Scripting API to write coordinated effects scripts.
For example, for a damage spell, the mechanism might be:
- User selects a target by clicking on it, and then presses a key to cast the spell (use the key bindings mechanism to associate an action with a key.)
- The script bound to that key sends a message to the server to invoke the spell ability.
- The code on the server for handling that ability validates the request, for example, to make sure the target is within range, the caster has enough mana, and so on.
- The ability code computes the effect of the script and applies it to the server side game state.
- The ability code sends an InvokeEffect message to all players within visual range, so that their clients can play the effect associated with that ability. This message can contain parameters for the script.
- The coordinated effect script runs on each client, playing a timed sequence of animations, particle effects, sounds, and so on, under the control of the script.
A client script can also invoke a coordinated effect from a key binding script, for example, by calling the function ClientAPI.InvokeEffect().
Implementing effects
A coordinated effect script is defined by a Python class. Invoking the effect creates an instance of the class and calls its ExecuteEffect() method.
To create a coordinated effect script, define a Python class with:
- a class constructor
- an
ExecuteEffect()method.
In a future release, you will also be able to define:
- an
UpdateEffect()method. - a
CancelEffect()method.
Class constructor
Usually, a server message invokes a coordinated effect, which calls the class constructor. An object's class constructor creates an instance of the object and initializes it.
A coordinated effect constructor has a single argument, the unique object ID that identifies the particular instance of the effect. The server uses the object ID to name a particular instance of an effect in later messages to update or cancel the effect.
The ExecuteEffect method
After creating an instance of a coordinated effect object, invoking the effect calls the ExecuteEffect() method. This method can take any arguments that the effect requires.
Pausing execution of the effect
The ExecuteEffect() method can use the Python yield statement to pause execution of the script. The yield statement takes one integer argument, the number of milliseconds that the script pauses before continuing.
For example, the statement:
yield 2500
will pause the effect script for 2.5 seconds before continuing. Use the yield statement to control the timing of various parts of the effect script.
Important: The ExecuteEffect() method must call yield directly to achieve the desired effect. If ExecuteEffect() calls another method or function that calls yield, the other method or function will be a normal generator function and won't pause the execution of the effect script.
Cleaning up after execution
Each coordinated effect object is executed only once, so before exiting ExecuteEffect() must clean up any resources it allocates. Standard Python and .NET garbage collection will clean up most objects, but many objects created with the Client Scripting API create and reference external resources. You must free such references explicitly with the Dispose() method.
The UpdateEffect method
This method is not currently used, but a future release of the Multiverse platform will use it to enable the server to send new data to an already running effect.
The CancelEffect method
This method is not currently used, but a future release of the Multiverse platform will use it to enable the server to cancel an already running effect.
Registering effects
To make an effect accessible to other scripts and the server, register it using ClientAPI.RegisterEffect().
Typically, you regsiter effects when you initially import the module containing the effects class.
Invoking coordinated effects
You can invoke a coordinated effect from the client or from the server.
Invoking with Client code
To invoke a coordiated effect on the player's client only, call ClientAPI.InvokeEffect(). This method does not communicate with the server, and so is not suitable for coordinated effects that you want other players to see.
For example, to invoke the simple example effect, use the following client script call:
effectArgs = {'message': 'This is a test log message.'}
ClientAPI.InvokeEffect("TestLogEffect", ClientAPI.GetLocalOID(), effectArgs)
The first argument to the method is a string which is the name of the effect. The second argument is a unique object ID that represents this instance of the effect. In a future release this object ID will be used to cancel or modify an effect that is already running. The third argument is a dictionary that contains the names and values of the arguments for the ExecuteEffect() method of the effect being invoked.
To invoke a coordinated effect that can also be seen by other players, use the ClientAPI.Network.SendTargetedCommand() method. For example:
ClientAPI.Network.SendTargetedCommand(ClientAPI.GetPlayerObject().OID, "/laugh")
Then define the command handler in a server script. For example, in
multiverse/config/sampleworld/extensions_proxy.py you might define:
class LaughCommand (ProxyPlugin.CommandParser):
def parse(self, cmdEvent):
playerOid = cmdEvent.getObjectOid()
Log.debug("/laugh: oid=" + str(playerOid))
AnimationClient.playSingleAnimation(playerOid,
"laugh")
return None
This script calls AnimationClient.playSingleAnimation()
which:
- Creates a cooridnated effect named "PlayAnimation."
- Sends the sourceOid parameter to the client coordinated effect script.
- Adds an "animName" argument to pass to the client effects script.
- Invokes the coordinated effect the sourceOid.
Invoking with server code
Here's a simple example of how to directly invoke a coordinated effect. You can put this example in proxy.py:
heartsEffect = CoordinatedEffect("SpellTargetEffect")
heartsEffect.sendTargetOid(1)
class ParticleExampleCommand (ProxyPlugin.CommandParser):
def parse(self, cmdEvent):
playerOid = cmdEvent.getObjectOid()
targetOid = cmdEvent.getTarget()
heartsEffect.invoke(playerOid, targetOid)
proxyPlugin.registerCommand("/cfxtest", ParticleExampleCommand())
For information on how to invoke a coordinated effect from the server, see Invoking a coordinated effect with an ability.
For information on the coordinated effect server object, see the multiverse.mars.objects.CoordinatedEffect API Javadoc.
Simple example effect
The simple example coordinated effect included with the Multiverse Sampleworld writes a message to the client chat window and log file when it is invoked.
The Sampleworld asset repository includes:
- Definition of the simple example effect in
Scripts/TestLogEffect.py - A key binding (Alt-Ctrl-G) to invoke the effect.
The following sections explain the code in TestLogEffect.py.
The first line imports the ClientAPI module, required to access to the client scripting API.
import ClientAPI
The next line defines the name of the Python class:
class TestLogEffect:
The methods below are in the scope of the class.
#
# Class constructor
#
def __init__(self, oid):
# save the instance oid for this instance of the effect
self.OID = oid
An object constructor in Python is always called __init__().
#
# This method is called to cancel the effect.
# XXX - Not supported in Multiverse Platform Beta 2
#
def CancelEffect(self):
pass
#
# This method is called to update the effect.
# XXX - Not supported in Multiverse Platform Beta 2
#
def UpdateEffect(self):
pass
The CancelEffect() and UpdateEffect() methods are not currently used, but will be used in a future version of the Client API to allow effects that can be updated by the server while executing, and effects that can be cancelled while playing.
#
# This method is called to execute the effect.
#
def ExecuteEffect(self, message):
# write the unique object ID of this effect instance, along with the
# message parameter to the client chat window and the log file
ClientAPI.Write('Effect Instance: ' + str(self.OID) + ' Message: ' + message)
The ExecuteEffect() method does the work of the effect. In this case it just writes a message to the chat window.
# register the effect
ClientAPI.RegisterEffect("TestLogEffect", TestLogEffect)
The call to ClientAPI.RegisterEffect() must be in the top scope of the module, not the scope of the class definition, so that it is executed when the module is imported.
Writing and testing your own effects
This section provides a step by step walkthrough of the process of creating your own coordinated effect using an effect called MyEffect as an example. When testing a new effect, it is convenient to create a temporary key binding to execute the effect. Once the effect is running the way you want it, remove the key binding and write server code to invoke the effect as part of an ability; see Invoking a cooridnated effect with an ability for more information.
- Write your effect script in the file
MyEffect.py. Some things to keep in mind when writing your effect:- Import the
ClientAPImodule to gain access to the multiverse client API. - Implement the functionality of your effect in the
ExecuteEffect()method. - Call
ClientAPI.RegisterEffect()to register your effect with the system.
- Import the
- Copy
MyEffect.pyto theScriptsdirectory of your asset repository. - Import and register your effect. This is done by adding the following line to
Startup.py:import MyEffect
- Create a key binding that calls
ClientAPI.InvokeEffect()to run your effect. For information on creating a key binding, see Customizing Key Bindings. - Run the client and use the key binding to execute your effect.
Debugging coordinated effects
Current debugging support for client scripts is primitive. Here are a few hints for debugging.
Use ClientAPI.Write() or ClientAPI.Log() at the start and end of your effects while debugging, so that you know that they are running to completion. Make sure to remove these statements once your effect is working, so that they don't clutter the logs, leave confusing chat messages for users, and lower performance.
When a script fails, the client will generate an exception write it to the log file (trace.txt). Search this file for the word "Exception" to find a stack trace that indicates where the error occurred, and what the problem was.
Other examples
Intermediate examples
TBD.
