The previous section explained how ActionScript can call into C++. This section describes how to communicate in the opposite direction, using Scaleform functions that enable the C++ program to communicate with a Flash movie. Scaleform supports C++ functions to directly get and set ActionScript variables (simple types, complex types and arrays) as well as invoke ActionScript subroutines.
The Direct Access API provides a more convenient and efficient interface to access and manipulate ActionScript variables from C++. For more information on this interface, please see the Direct Access API section.
Scaleform supports GetVariable and SetVariable, which enable direct manipulation of ActionScript variables. For performance critical use cases, please refer to the section 2 on the Direct Access API for a more efficient way to modify variables and object properties. Although Set/GetVariable are not recommended for performance reasons, there are some cases where they are more convenient to use than the Direct Access API. For instance, there’s no need to use the Direct Access call GFx::Value::SetMember to set a single value once during the lifetime of the application, especially if the target is at a deep nesting level. Also, obtaining references to an ActionScript object is usually done via GetVariable – it is called once to get a GFx::Value reference which is then used for Direct Access operations.
The following example demonstrates incrementing an ActionScript counter using GetVariable and SetVariable:
int counter = (int)pHUDMovie ->GetVariableDouble("_root.counter"); counter++; pHUDMovie->SetVariable("_root.counter", Value((double)counter));
GetVariableDouble returns the value of the _root.counter variable automatically converted to the C++ Double type. Initially, the variable does not exist and GetVariableDouble returns zero. The counter is then incremented on the next line and the new value is saved to _root.counter using SetVariable. The online documentation for GFx::Movie lists the different variations of GetVariable and SetVariable.
SetVariable has an optional third argument of type GFx::Movie::SetVarType that declares the assignment “sticky.” This is useful when the variable being assigned has not yet been created on the Flash timeline. For example, suppose that the text field _root.mytextfield is not created until frame 3 of the movie. If SetVariable(“_root.mytextfield.text”, “testing”, SV_Normal) is called on frame 1, right after the movie is created, then the assignment would have no effect. However, if the call is made with SV_Sticky (the default value) then the request is queued up and applied once the _root.mytextfield.text value becomes valid on frame 3. This makes it easier to initialize movies from C++. Keep in mind that, generally, SV_Normal is more efficient than SV_Sticky, so SV_Normal should be used where possible.
To access arrays of data, Scaleform provides the functions SetVariableArray and GetVariableArray.
SetVariableArray sets array elements in specified range to data items of specified type. If the array does not exist, it is created. If an array already exists but does not contain enough items, it is resized appropriately. However, setting a number of elements less than the current size of the array will not cause the array to resize. GFx::Movie::SetVariableArraySize sets the size of the array, which is useful when setting an existing array with fewer elements than it had before.
The following example demonstrates the use of SetVariableArray to set an array of strings in AS:
int idxInASArray = 0; const char* strarr[2]; strarr[0] = "This is the first string"; strarr[1] = "This is the second one"; pMovie->SetVariableArray(Movie::SA_String, "_root.strArray", idxInASArray, strarr, 2);
The following is another variant of the previous example, using wide-strings:
int idxInASArray = 0; const wchar_t* strarr[2]; strarr[0] = "This is the first string"; strarr[1] = "This is the second one"; pMovie->SetVariableArray(Movie::SA_StringW, "_root.strArray", idxInASArray, strarr, 2)
The GetVariableArray method fills the provided data buffer with results from an AS array. The buffer must be big enough to hold the number of items requested.
In addition to modifying ActionScript variables, ActionScript methods can be invoked using the GFx::Movie::Invoke() method. This is useful for performing more complicated processing, triggering animation, changing the current frame, programmatically changing the state of UI controls, and dynamically creating UI content such as new buttons or text. For performance critical use cases, please refer to the section on the Direct Access API for a more efficient way to invoke methods and control action flow.
Example:
The following ActionScript function can be used to set the slider grip position relative to the range.
Assume that the ‘mySlider’ object exists in the _root level. The SetSliderPos is defined inside the "mySlider" object as follows:
AS Code: this.SetSliderPos = function (pos) { // Clamp the incoming position value if (pos<rangeMin) pos = rangeMin; if (pos>rangeMax) pos = rangeMax; gripClip._x = trackClip._width * ((pos-rangeMin)/(rangeMax-rangeMin)); gripClip.gripPos = gripClip._x; }
where the "gripClip" is a nested movie clip in "mySlider" and pos always lives within the rangeMin/Max.
In the user application, the Invoke function is used as follows:
C++ Code:
Value result; bool bInvoked = pMovie->Invoke("_root.mySlider.SetSliderPos", &result, "%d", newPos);
Invoke returns true if the method was actually invoked or false otherwise.
When invoking an ActionScript function, you must make sure that it is loaded. One common error in using Invoke is calling an ActionScript routine that is not yet available, in which case an error will be printed to the Scaleform log. An ActionScript routine will not become available until the frame it is associated with has been played or the nested object it is associated with has been loaded. All ActionScript code in frame 1 will be available as soon as the first call to GFx::Movie::Advance is made, or if GFx::MovieDef::CreateInstance is called with initFirstFrame set to true.
Along with Invoke, the GFx::Movie::IsAvailable() method is typically used to ensure the AS function exists before it is invoked:
Value result; if (pMovie->IsAvailable("parentPath.mySlider.SetSliderPos")) pMovie->Invoke("parentPath.mySlider.SetSliderPos", &result, "%d", newPos);
This example used the ‘printf’ style of Invoke. In this case Invoke takes arguments as a variable argument list described by a format string. Other versions of the function use GFx::Value to efficiently process non-string arguments. For example:
Value args[3], result; args[0].SetNumber(i); args[1].SetString("test"); args[2].SetNumber(3.5); pMovie->Invoke("path.to.methodName", &result, args, 3);
InvokeArgs is identical to Invoke except that it takes a va_list argument to enable the application to supply a pointer to a variable argument list. The relationship between Invoke and InvokeArgs is similar to the relationship between printf and vprintf.
When accessing Flash elements from within the user application, fully qualified paths are often used to lookup objects.
The following GFx::Movie functions rely on fully qualified paths:
Movie::IsAvailable(path) Movie::SetVariable(path, value) Movie::SetVariableDouble(path, value) Movie::SetVariableArray(path, index, data, count) Movie::SetVariableArraySize(path, count) Movie::GetVariable(value, path) Movie::GetVariableDouble( path) Movie::GetVariableArray(path, index, data, count) Movie::GetVariableArraySize(path) Movie::Invoke(pathToMethodName, result, argList, ...)
In order for nested object paths to resolve correctly, all parent movie clips must have unique instance names. Nested object names are separated using the dot "." and are case sensitive, as shown below.
In the following example, consider a movie clip with the instance name "clip2" nested inside another movie clip named "clip1", which is sitting on the main stage.
Main Stage -> clip1 -> clip2
Valid paths are:
"clip1.clip2" "_root.clip1.clip2" "_level0.clip1.clip2"
Invalid paths are:
"Clip1.clip2" <- case mismatch - "Clip1" must be lowercase "clip2" <- missing parent"clip1." - path name.
The "_root" and "_level0" names are optional in ActionScript 2 and can be used to force specific base level look up. However, they are required in ActionScript 3 as by default the lookup will occur at the stage level. For backward compatibility we have provided aliases in ActionScript 3 to access root with the following names: _root, _level0, root, level0. When loading separate SWF files into levels, _root will refer to the base of the current level, while specifying a _levelN (using the syntax shown above) allows you to select a specific level.
NOTES:
You can check paths to objects and variables in Flash Studio's Test Movie (Ctrl-Enter) environment, by pressing (Ctrl-Alt-V) (variables).
The timeline sequence in Flash Studio, beginning with Scene 1 link, does NOT dictate the target path. Certain elements are listed which are not actually used in the object path. Use (Ctrl-Alt-V) (variables) popup to check the actual valid paths.
When loading movies within movies, using the loadMovie command, you may encounter problems due to objects changing levels. Objects which did exist on _level0 are now on _level1 or _level2 (depending on what Level you loaded to), or within a targeted Movie Clip. To reference an Object on another layer, simply use: _levelN.objectName or _levelN.objectPath.objectName, where N is the Level number.
Many elements of this section were adopted from the following two path tutorials, which we recommend reading: