Our game Pablo and the Puppet Punch needed a lot of animations to play on screen at once along with an inverse kinematic implementation and a whole lot of VFX. We looked for options for implementing Skeletal Animations in our game. Skeletal Animation is one of the best ways to optimize the total size of your game for mobile devices such as iPhone and iPad.
For understanding what skeletal animation is and how it can help you, read this blog
This implementation is in C#; but unfortunately he did not share the exporter he used for converting flash animations to XML that can be read in code.
No point being sad, we continued our search for different skeletal animation tools which could help us. Another promising breakthrough was “Demina”. Again, a C# implementation based on bone structure, it looked promising at first sight as it had a good UI for getting your animation in place. The only concern is that you need a “mouse” as the control to move a bone is through middle mouse button. Not a difficult requirement though! But since you have the source, you can quickly change this input control and make it an easy drive for games created in XNA etc.
But we needed something that could serve our Cocos2D environment. We got through this tutorial on Cocos2D forum called TDAnimEngine which creates 2D characters in Maya and converts it into Cocos2D nodes. Maya sounded a little threatening but still we took a chance but there were many faults in the implementation of given source and none of us being an expert in mel script, it became a headache for us to get this thing working.

TDAnimEngine Implementation in Maya
If you want to try, download this version as a lot of things have been fixed in this one. Still there were some bugs in positioning the character, and Haykhelped me figure that out. At first please make sure to build your necessary object using simple planes and add as textures your PNG files which you’re going to use in your iOS project. This is his changed version of MayaToCocos2DNodes.mel file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
string $master = "Body";
string $sel[] = `ls -selection`;
// Master position
float $originalPos[] = `xform -ws -q -t ($master)`;
string $children[] = `ls -selection`;
print ("<root>\n<nodesConfig>\n");
$mat = getSurfaceShader($master);
$fileText = getTextureFromMaterial($mat);
$fileName = filepart(`getAttr ($fileText+".fileTextureName")`);
float $width = `getAttr ($fileText+".outSizeX")`/100;
float $height = `getAttr ($fileText+".outSizeY")`/100;
float $ppoint[] = `xform -q -t ($master+".rotatePivot")`;
float $px = $ppoint[0];
float $py = $ppoint[1];
float $apX = ($width/2+$px)/$width;
float $apY = ($height/2+$py)/$height;
float $rot[] = `getAttr ($master+".r")`;
float $scl[] = `getAttr ($master+".s")`;
print ("\t<node id='"+$master+"' parent='root' x='"+$originalPos[0]+"'
y='"+$originalPos[1]+"' image='"+$fileName+"' apx='"+$apX+"' apy='"+$apY+"' rz='"+$rot[2]+"' sx='"+$scl[0]+"' sy='"+$scl[1]+"' w='"+$width+"' h='"+$height+"' />\n");
$parentnode = $master;
for ($o = 0; $o < `size $children`; $o++)
{
if ($children[$o] != $master)
{
// -------------------------------------------------------------
// GET POSITION
string $parent[] = `listRelatives -p ($children[$o])`;
float $pos[] = `getAttr ($children[$o]+".t")`;
float $rot[] = `getAttr ($children[$o]+".r")`;
float $scl[] = `getAttr ($children[$o]+".s")`;
// -------------------------------------------------------------
// GET ANCHOR POINT
float $ppoint[] = `xform -q -ws -t ($children[$o]+".rotatePivot")`;
float $px = $ppoint[0];
float $py = $ppoint[1];
$mat = getSurfaceShader($children[$o]);
$fileText = getTextureFromMaterial($mat);
$fileName = filepart(`getAttr ($fileText+".fileTextureName")`);
//print ("\n"+$children[$o]+"\n"+$px + "\n" + $py);
// -------------------------------------------------------------
// OUTPUT
print ("\t<node id='"+$children[$o]+"' parent='"+$parent[0]+"' image='"+$fileName+"' x='"+$pos[0]+"' y='"+$pos[1]+"' z='"+$pos[2]+"' rz='"+$rot[2]+"' sx='"+$scl[0]+"' sy='"+$scl[2]+"' apx='"+$px+"' apy='"+$py+"' />\n");
}
}
print ("</nodesConfig>\n</root>");
// HELPER METHODS -------------------------------------------------------------------
proc string getSurfaceShader (string $objName)
{
string $myShapeNode[] = `listRelatives -children -shapes $objName`;
string $mySGs[] = `listConnections -type shadingEngine $myShapeNode[0]`;
string $surfaceShader[] = `listConnections ($mySGs[0] + ".surfaceShader")`;
return $surfaceShader[0];
}
proc string rootNode( string $object )
{
string $buffer[];
tokenize $object "." $buffer;
return $buffer[0];
}
proc string getTextureFromMaterial( string $material )
{
string $texture = "";
string $class[] = getClassification( `nodeType $material` );
if ( "shader/surface" == $class[0] && `connectionInfo -id
( $material + ".color" )` )
{
$texture = rootNode( `connectionInfo -sfd ( $material + ".color" )` );
}
return $texture;
}
global proc string filepart( string $path )
// Extracts the path portion of an absolute filepath.
// Input: e.g. "D:/projects/default/scenes/myScene.mb"
// Result: e.g. "myScene.mb"
//
// Filepath can be delimited with
// either slash ("/" or "\")
{
string $filepart = match( "[^/\\]*$", $path );
return $filepart;
};
|
We did not try this but we are sharing this as it may be of use to someone who needs a solution.
Also , In getTextureFromMaterial function value of $class[0] is “drawdb/shader/surface/lambert:shader/surface” instead of “shader/surface”.
Once you make these changes it should work for you. If it doesn’t , don’t get disappointed and keep reading .
Along with this solution, we came across an excellent tool called grapefrukt exporter.( Hayk suggested it! ) I call it excellent as it finally solved all our troubles though it was a little difficult to start with. And the most important benefit was it uses Flash and not Maya.
Below we want to share with you quickly how you can set up grapefrukt exporter and convert the XML data it generates into cocos2D nodes. You can watch this video made by the developer of this exporter to have a quick idea of what grapefrukt can do for you. To read the tutorial on how to exactly use grapefrukt, read the second part of this series here.
If you any other awesome tools for skeletal animation, please share the links in comments below. We hope this will be of use to young developers like us.
Signing off,
Team MechMocha