Some Animation Problems
by Max vE · in Torque 3D Professional · 07/26/2014 (2:44 am) · 5 replies
Hey it's me again, back with another issue haha. this time it's about animation priorities. Really hoping I'm not missing something obvious this time, and I suck at getting just the essential info across, so this might take a while to explain. Basically, I have a player character that doesn't use guns. It only does melee attacks. Also, I want the character to only be able to play one animation at a time, no blend animations. I have it set up with root, run, sprint_forward, attack1, attack2 etc animations. When the player left clicks, a command is sent to the server and the player character stops moving and plays one of the attack animations through setActionThread(). I use this over playThread() because it does proper transitions between animations. When the character is stationary and they left click, it plays the attack animations totally fine. The problem is when they are either running or sprinting and then left click. Instead of the character stopping and playing an attack animation, they just stop and display the root pose. By the way, the way that the character stops is by setting setMoveSpeed(0), that function I moved from AIPlayer to player using code that I saw in another thread. Pretty sure that's why it begins playing the root pose, because movement is 0. I don't see why standing still should let it play fine though... Anyway, I thought maybe by setting the priority of the root pose lower and giving the attack animation a higher priority should fix the problem, but no. The character still goes ahead and plays the root pose. Any idea what I can do to fix this?
About the author
#2
07/26/2014 (5:57 pm)
Thanks for the reply. When the move speed is set to 0, I think there is a really short transition time from 1 to 0 speed. Just for a bit of extra info, for now I have it set up so that after 1 second from left clicking, the setMoveSpeed is set back to 1, so either the player can stay standing still and the animation will finish, or they can begin moving forward again and the animation will transition to the run one. I tried adding a scheduled delay to the animation with several different values, but unfortunately none of them seemed to work. When a player is moving forward and left clicks, they will stop for a second in root pose, and then go back to moving forward with run animation. However, with a delay, if they left click while moving, and release 'W' before the scheduled animation begins, they will play the attack animation. Essentially, this is the same as standing still and clicking, because the player isn't holding down 'W'. So therefore it must be something to do with holding down the 'W' key. Maybe by holding it down, it is constantly refreshing the action animations? I thought that by giving the attack animations a higher priority that would fix it, but it still doesn't. Any ideas now?
#3
07/27/2014 (7:07 am)
Interesting. Okay lets take a different approach and try intercepting the movement code. In your "scripts/client/default.bind.cs" there is already a "setSpeed" function, but it isn't very useful because it doesn't effect the player until the movement key is released. In my game I too need to freeze the player in his tracks (for a stun effect), and so I modified this file slightly. Starting from the line:$movementSpeed = 1; // m/suntil the end of the "movebackward" function, take a look at this:
//------------------------------------------------------------------------------
// Movement Keys
//------------------------------------------------------------------------------
//I like setSpeed(). However, "$movementSpeed" is only used upon the first
//press of a movement key. This means changing the player's speed won't be
//noticed until the player stops moving and then starts again. We'll use
//these triggers to mark if the player is moving.
//----------------------------------
$mvIsForwardAction = false;
$mvIsBackAction = false;
$mvIsLeftAction = false;
$mvIsRightAction = false;
//----------------------------------
$movementSpeed = 1; // m/s
function setSpeed(%speed)
{
//if(%speed) //commented out so that a 0 movement speed is possible
$movementSpeed = %speed;
//refresh the player's speed if already moving
if ($mvIsForwardAction)
moveforward(1);
if ($mvIsBackAction)
movebackward(1);
if ($mvIsLeftAction)
moveleft(1);
if ($mvIsRightAction)
moveright(1);
}
function moveleft(%val)
{
$mvLeftAction = %val * $movementSpeed;
$mvIsLeftAction = %val;
}
function moveright(%val)
{
$mvRightAction = %val * $movementSpeed;
$mvIsRightAction = %val;
}
function moveforward(%val)
{
$mvForwardAction = %val * $movementSpeed;
$mvIsForwardAction = %val;
}
function movebackward(%val)
{
$mvBackwardAction = %val * $movementSpeed;
$mvIsBackAction = %val;
}So now just try using "setSpeed(0)" to stop the player, play your animation (probably still with a delay to make sure you've stopped), and then call "setMoveSpeed(1)" a second or two later. I'm a huge fan of this method because it stops movement triggers from even being set instead of having to decide what to do when they are set. Let me know how it goes. If it doesn't help then we'll have to dig deeper!
#4
07/28/2014 (2:25 am)
That seemed to work surprisingly well! By the looks of it, it is all running correctly. Basically I added the code you posted above, and set setSpeed to 0 when a left click was made, along with a command to server for animation. Using PlayerData::animationDone, I would then send a command back to client, returning speed to 1 after the attack animation finishes. Holding down 'W' no longer prevents animations from playing. I've basically removed setMoveSpeed now because it is unnecessary with setSpeed. However, if you feel like answering, I have a few more questions. Briefly, how do the functions that you posted above actually get called and run? And also, why is there such a function as setSpeed in client scripts? It seems odd to be able to control the speed of a player on the client side, when everything else affecting gameplay runs from server scripts. And lastly, would the way I'm doing things now still hold up in a multi-player situation? Thanks for all of the help.
#5
07/28/2014 (7:23 am)
I'm glad to hear it! So there is inherently nothing special about the above functions. They work because they are bound to key presses. Further down in default.bind.cs is the line:moveMap.bind( keyboard, w, moveforward );You could just as easily call any other function when the "W" key is pressed instead. Calling it from a keybind gives information about if the key is up or down though. In those functions, "%val" will either be 1 (for pressed) or 0 (for released). The real magic is the "$mv..." global variables. $mvLeftAction, $mvRightAction, $mvForwardAction, and $mvBackwardAction (to name a few) are variables defined in your engine's "moveManager.cpp" file. Each of these script variables has a counterpart in the engine itself. For instance, changing the value of $mvForwardAction in script also changes the value of "mForwardAction" in the source. "getNextMove" from "moveList.cpp" makes use of these values, and you can trace the rest from there if you'd like. Once the engine has them, the values act as scalars and are multiplied in to give the player's actual move speed. This means you could just as easily use setSpeed(0.5) or setSpeed(2) to half or double your speed respectively. Now for the client server stuff. To put it simply, all of this has to start on the client side because the client first has to decide to move or not. This information is then sent to the server which does much of the real work, but it wouldn't know if you pressed the move forward key unless you actually tell it. So far we haven't violated the client server relationship. As long as you make the proper command to server and command to client calls it should work great in multiplayer games.
Torque Owner Caleb
Default Studio Name