To make perfect cyclic animations using turn <angle> speed <speed> * RUN_SPEED, simply use these easy templates to teach yourself the process. This is a great way to get rid of thousands of laggy lines of animation code in your projects, leading to better gameplay and smoother animation.
For example:
In step 1, we rotate a part:
turn Part to x-axis <90> speed <?>*RUN_SPEED;
Note that we've left the speed unknown at this time.
Now, let's do the second half of the animation (I'm assuming a matched set of two pairs, for best efficiency with things like two-legged walkers, etc., but you can extrapolate this to any combination of Pieces).
turn Part to x-axis <-30> speed <120>*RUN_SPEED;
Note that we have a speed now! That's because I subtracted the second rotation from the first one, giving me a proper scalar speed!
So, if all we wanted to do was a simple, cyclic animation with exactly two parts, it'd be written like this:
Code: Select all
Argh's GPL Very Basic Circular Animation Cycle Code
All code within this example, and the method described, is hereby released under the GPL.
CyclicAnimation()
{
While(TRUE)
{
if(bMoving)
{
turn Part to x-axis <90> speed <120>*RUN_SPEED;
wait-for-turn Part around x-axis;
}
if(bMoving)
{
turn Part to x-axis <-30> speed <120>*RUN_SPEED;
wait-for-turn Part around x-axis;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if(!bMoving)
{
turn Part to x-axis <0> speed <300>*RUN_SPEED;
sleep 500;
}
}
}
But, wait! Most people are wanting to do two animations at the same time, mirrored. Like walking animations, for example (again, this can be applied anywhere you want to mirror things). This is a little more complicated, because we need to mirror the two parts in series, so that each mirror remains in lockstep with one another. Moreover, there will be times when, to lock the animation into perfect synch and never allow it to desynchronise, we need to be very careful that the fastest-moving part (the one that travels the furthest) is always watched.
So, let's look at an example. We'll use four Pieces: R_Thigh, R_Leg, L_Thigh, L_Leg.
Here is the first iteration. We're just blocking out the steps.
Code: Select all
Argh's GPL Basic Circular Animation Cycle Code, First Iteration
All code within this example, and the method described, is hereby released under the GPL.
WalkAnimation()
{
While(TRUE)
{
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////RIGHT LEG BACKS, LEFT LEG RISES
if(bMoving)
{
turn R_Thigh to x-axis <15> speed <?>*RUN_SPEED;
turn R_Leg to x-axis <10> speed <?>*RUN_SPEED;
turn L_Thigh to x-axis <-20> speed <?>*RUN_SPEED;
turn L_Leg to x-axis <10> speed <?>*RUN_SPEED;
wait-for-turn ? around x-axis;
}
//////////////////////////////////////////////////////////////////////////////////////RIGHT LEG FOLDS FORWARDS, LEFT LEG HITS GROUND
if(bMoving)
{
turn R_Thigh to x-axis <-10> speed <?>*RUN_SPEED;
turn R_Leg to x-axis <68> speed <?>*RUN_SPEED;
turn L_Thigh to x-axis <0> speed <?>*RUN_SPEED;
turn L_Leg to x-axis <0> speed <?>*RUN_SPEED;
wait-for-turn ? around x-axis;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////LEFT LEG BACKS, RIGHT LEG RISES
if(bMoving)
{
turn R_Thigh to x-axis <-20> speed <?>*RUN_SPEED;
turn R_Leg to x-axis <10> speed <?>*RUN_SPEED;
turn L_Thigh to x-axis <15> speed <?>*RUN_SPEED;
turn L_Leg to x-axis <10> speed <?>*RUN_SPEED;
wait-for-turn ? around x-axis;
}
//////////////////////////////////////////////////////////////////////////////////////LEFT LEG FOLDS FORWARDS, RIGHT LEG HITS GROUND
if(bMoving)
{
turn R_Thigh to x-axis <0> speed <?>*RUN_SPEED;
turn R_Leg to x-axis <0> speed <?>*RUN_SPEED;
turn L_Thigh to x-axis <-10> speed <?>*RUN_SPEED;
turn L_Leg to x-axis <68> speed <?>*RUN_SPEED;
wait-for-turn ? around x-axis;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if(!bMoving)
{
wait-for-turn ? around x-axis;
wait-for-turn ? around x-axis;
turn R_Thigh to x-axis <0> speed <300>*RUN_SPEED;
turn R_Leg to x-axis <0> speed <300>*RUN_SPEED;
turn L_Thigh to x-axis <0> speed <300>*RUN_SPEED;
turn L_Thigh to x-axis <0> speed <300>*RUN_SPEED;
sleep 500;
}
}
}
Also, in case it's not obvious, the Piece we *always* want to wait-for-turn on is the Piece that has to move the furthest (and the fastest). This prevents Spring from borking the animation (for whatever reasons, it doesn't seem to treat turns of different speeds quite the same way). And the !bMoving state should use the pairs of Pieces named, when you've figured out what the fastest Pieces are. Note that, with a cyclic animation using two bodies that both go through the same cycle, you'll always end up with exactly two Pieces named. If you don't, you've done something wrong!
Code: Select all
Argh's GPL Basic Circular Animation Cycle Code, Final Iteration
All code within this example, and the method described, is hereby released under the GPL.
WalkAnimation()
{
While(TRUE)
{
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////RIGHT LEG BACKS, LEFT LEG RISES
if(bMoving)
{
turn R_Thigh to x-axis <15> speed <15>*RUN_SPEED;
turn R_Leg to x-axis <10> speed <10>*RUN_SPEED;
turn L_Thigh to x-axis <-20> speed <10>*RUN_SPEED;
turn L_Leg to x-axis <10> speed <58>*RUN_SPEED;
wait-for-turn L_Leg around x-axis;
}
//////////////////////////////////////////////////////////////////////////////////////RIGHT LEG FOLDS FORWARDS, LEFT LEG HITS GROUND
if(bMoving)
{
turn R_Thigh to x-axis <-10> speed <25>*RUN_SPEED;
turn R_Leg to x-axis <68> speed <58>*RUN_SPEED;
turn L_Thigh to x-axis <0> speed <20>*RUN_SPEED;
turn L_Leg to x-axis <0> speed <10>*RUN_SPEED;
wait-for-turn R_Leg around x-axis;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////LEFT LEG BACKS, RIGHT LEG RISES
if(bMoving)
{
turn R_Thigh to x-axis <-20> speed <10>*RUN_SPEED;
turn R_Leg to x-axis <10> speed <58>*RUN_SPEED;
turn L_Thigh to x-axis <15> speed <15>*RUN_SPEED;
turn L_Leg to x-axis <10> speed <10>*RUN_SPEED;
wait-for-turn R_Leg around x-axis;
}
//////////////////////////////////////////////////////////////////////////////////////LEFT LEG FOLDS FORWARDS, RIGHT LEG HITS GROUND
if(bMoving)
{
turn R_Thigh to x-axis <0> speed <20>*RUN_SPEED;
turn R_Leg to x-axis <0> speed <10>*RUN_SPEED;
turn L_Thigh to x-axis <-10> speed <25>*RUN_SPEED;
turn L_Leg to x-axis <68> speed <58>*RUN_SPEED;
wait-for-turn L_Leg around x-axis;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if(!bMoving)
{
wait-for-turn L_Leg around x-axis;
wait-for-turn R_Leg around x-axis;
turn R_Thigh to x-axis <0> speed <300>*RUN_SPEED;
turn R_Leg to x-axis <0> speed <300>*RUN_SPEED;
turn L_Thigh to x-axis <0> speed <300>*RUN_SPEED;
turn L_Thigh to x-axis <0> speed <300>*RUN_SPEED;
sleep 500;
}
}
}
P.S.: There's no point in using pre-processor stuff in Spring. I've done some tests, and it simply makes zero real difference in speed.
P.P.S.: There's no point in using stored constants to do the mirroring. In fact, it'd be a bit faster to name RUN_SPEED as a var. However, I have found that that means Spring sometimes misses it, when it reads only X lines of the COB during a frame, so I do not recommend it, in a script without discreet Sleep events. I've done some tests, and it turns out that, in Spring at any rate, it takes more CPU to go grab the constants and return them during every if{} loop than you ever get back by saving a little bit of speed not setting the values in the lines themselves, due to the byte-code nature of COB scripts, and the fact that Scriptor doesn't write very efficient COBs. I figured I might get arguments to the contrary on that, but go write a proof with a hundred iterations or constants to mirror (I did) and you'll start to see what I mean- the speed problems stack up reeeeeally quickly, and are obvious.
P.P.P.S.: Yes, whenever possible, the code ninja eliminates lines that don't actually do anything! However, this method tends to help you eliminate those areas really quickly, because you'll see them during the blocking-out phase, when your code is still pretty loose and you're just making sure you have everything laid out in a way that makes sense. And yes, a line of BOS that does, effectively, nothing, is just as slow as a line that does something!
P.P.P.P.S.: Isn't this a lot more useful than e-drama? Go get work done on your mods, people!