Flexible Talkanimations

  • #1, by SuroSunday, 09. October 2011, 15:58 13 years ago
    This is kind of an oldy, but a goody I figured out thanks to Mowren, BrokenClaw and Einzelkaempfer (from the german boards). So I thought it would be a nice thing to (finally) share it with the english side ofr the boards as well.

    First of all, I had this problem:

    I have a project with at least 3 characters. Every character has a talkanimation in 8 different directions, made out of 6 frames each. Having to loop after 6 frames doesn't look right to me since everybody looked like they nod to some rythm. I also wanted to avoid to randomize the animations, since that would look REALLY odd sometimes. So I put the 6 frames in a different order until I had a 18 frame long animation. The nodding was reduced to something decent looking. However, the big problem was, that I had to do this for the other 7 directions, and for the other characters. I think sorting out over 400 frames manually is kind of exhausting, which is why I decided to use a script for that. It hat to accomplish those things:

    - I have to understand it - I have to be able to change the order of the frames easily - the script has to work for every direction

    With that in mind, let's get started:

    I create a "definition script" This is really easy. Just create a script, look for the tab that sais "Properties" and check "definition script" instead of "execution script". Here is the "definition script" which I called "Talkanimation_Split":

    
    zahlenreihe = "264357234576234567" 
    cf = 1               

    function split(string_to_be_splitted) splitted = {} for i=1, string.len(string_to_be_splitted) do splitted[i] = tonumber(string.sub(string_to_be_splitted, i, i )) end return splitted end

    frames = split(zahlenreihe)

    "Zahlenreihe" is german vor "row of numbers" (I guess) and is the order of frames I chose. You might notice that there is no 1 and that it goes to 7, even though I said I only have 6 frames. I'll come to that a little later. "cf=1" is the starting value of the "current frame" (I was lazy to type, so I shortened it down to cf). More to that also in the execution script later on. "Function split" has been made by BrokenClaw. It does something really neat. It takes every number in that string of numbers and puts them on its own with its own definition. Now I don't have a string of 18 numbers, but 18 sungle numbers. You could have done this with "frames = {2,6,4,3,5,7,2,3,4,5,7,6,2,3,4,5,6,7}" but then I wouldn't accomplish the "easy to change" portion of the script. If I now want a different order of Frames, I just changes the string of numbers.

    Next is the execution script, which I called "Talkanimation"

    
    direction = getObject("Characters[Cathlin]"):getInt(VCharacterDirection)

    if direction == 315 then talk = getObject("ActiveAnimations[Talk-RightDown]") end if direction == 270 then talk = getObject("ActiveAnimations[Talk-Down]") end if direction == 225 then talk = getObject("ActiveAnimations[Talk-LeftDown]") end if direction == 180 then talk = getObject("ActiveAnimations[Talk-Left]") end if direction == 135 then talk = getObject("ActiveAnimations[Talk-LeftUp]") end if direction == 90 then talk = getObject("ActiveAnimations[Talk-Up]") end if direction == 45 then talk = getObject("ActiveAnimations[Talk-RightUp]") end if direction == 0 then talk = getObject("ActiveAnimations[Talk-Right]") end

    talk:setValue(VAnimationFirstFrame, (frames[tonumber(cf)])) talk:setValue(VAnimationLastFrame, (frames[tonumber(cf)]))

    cf = cf + 1 if cf >= 19 then cf = 1 end

    This one is a bit longer, but really easy to explain. "direction = getObject("Characters[Cathlin]"):getInt(VCharacterDirection)" gets the current direction of the character (in my case Cathlin). Does the char look up, down, left or right?

    "if direction" also is easy to explain. The direction of a character in Visionaire is with degrees. You can actually change how detailed those degrees should be in the options. I only have a change of direction every 45° (8 different directions), so I had to only cover those 8 possibilities. So, IF the direction is 0° (in Visionaire meaning RIGHT), then " talk = getObject("ActiveAnimations[Talk-Right]")" uses the frames in the talkanimation for RIGHT.

    "talk:setValue(VAnimationFirstFrame, (frames[tonumber(cf)]))" This one is responsible for WHICH frame is shown. If you would write (frames[1]) it would show the FIRST frame, (frames[2]) the SECOND frame and so on. Since it was meant to be flexible, it uses the value "cf". This value will count from 1 to 18 (which is the number of frames I use), and then starts over at 1. This is done with the next simple line: "cf = cf + 1" It basically means "Whatever number you are at right now + 1" which means "Whaever frame you are at right now, take the next one".

    "if cf >= 19 then" is a little security, so "cf" is never ever above 18. We don't have a 19th frame.

    With this the work isn't done yet. We do have the scripts, but if we test right now, it doesn't work. ow should it? The scripts are not executed properly.

    I made 2 actions for that:

    Talk_Loopa:

    Call script "Talkanimation" Pause for [pause inbetween the frames, just take the value you took for the original talk animation] Call action Talk_Loopb

    Talk_Loopb:

    Call script "Talkanimation" Pause for [pause inbetween the frames, just take the value you took for the original talk animation] Call action Talk_Loopa

    Those two actions call each other, since Visionaire does not support that an action calls itself. Because of that action, a frame is shown for a fraction of a second, and changes to the next frame. Just like in the normal talk animation.

    The last step is to implement those two actions somehow. There is a neat feature to call an action on a certain frame of an animation. Just click on the very first frame, and click "edit frame" - it's the picture icon with the pencil. Und "Action" you just select your Talk_Loopa action.

    Last problem is, that the frame that calls the action is played by its own VERY often. This usually leads to the issue that the script doesn't work properly. What I did is simply duplicating the very first frame. The "original" animation has now 7 frames instead of 6 (since the first 2 frames are identical). This is also the reason why there is no 1 in the number string: to avoid the first frame (which calls the action) of being played when the script is running. It will play ONCE if the normal talkanimation starts, but the script will take over as soon as the first frame was played.

    Soooooo this was my first travel to the magical lands of LUA. I can imagin that there are questions here and there. Ask them! I hope I can answer them grin

    Newbie

    36 Posts


  • #2, by SuroSunday, 09. October 2011, 16:10 13 years ago
    Additional info:

    I had more than one character, and I had a little bug. As soon as I implemented this script for other characters as well, the whole contruct stopped working. Here is why:

    If there are more animations with the same name, it is purely random which animation will be found with getObject. It is important that all animations have unique Names.

    In my case I had every talk right animation for every character named "Talk_right". After I changed this to "Charname_Talk_right" and changed the script to reflect those changes, everything worked like a charm.

    Also, this might or might not be a step towards lypsinced animation. There where some tests by Einzelkampfer which are sadly online. He actually accomplished variable pauses inbetween the frames. Feel free to develop this idea further!

    Newbie

    36 Posts

  • #3, by NigecSunday, 09. October 2011, 16:31 13 years ago
    Nice work Suro! I try to avoid characters like the plaque (I suck at character animation) hence why I favour first person views, any npc's are fairly contained, but there is plans to do a 3rd person view so this will be very useful smile

    Key Killer

    627 Posts

  • #4, by SuroSunday, 09. October 2011, 19:56 13 years ago
    Haha like already said Mowren, BrokenClaw and Einzelkaempfer did most of the work, I just was the guy who kept pushing them :p

    Glad though you might be able to use it!

    Newbie

    36 Posts