Cyclic Animation for Newbies

Cyclic Animation for Newbies

Discuss game development here, from a distinct game project to an accessible third-party mutator, down to the interaction and design of individual units if you like.

Moderator: Moderators

User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Cyclic Animation for Newbies

Post by Argh »

Here's one of the things I've been doing lately. Thought I'd share.

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;
		}
	}
}
Where bMoving is a constant that is set to TRUE/FALSE by the script call to StartMoving() and StopMoving(), and RUN_SPEED is a constant, given a value during your Create() event.


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;
		}
	}
}
Ok, now it's time to derive the speeds, and decide which Pieces we're going to wait-for-turn on! This is really quite easy. The only hard part, frankly, is remembering to use the second cycle's final angle values to determine the speeds of the first cycle.

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!
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Post by KDR_11k »

What do you mean with preprocessor stuff not making a difference in speed? Preprocessor stuff is used for additional flexibility, e.g. a generic walkscript.h with the option to #define USE_ARMS to make the unit sway its arms while walking or an ammo script that can support any number of ammo types. Or just setting e.g. angles that get used more often in the script so you can change them in one place instead of looking through the script, finding every line that refers to that angle and changing it. I've even used it to make e.g. the drone script for CvC where I use a different #define to compile it and get a different unit's script.

Or do you mean using variables for the constants instead of #define? It may not make a noticeable difference but there's no freaking point in doing that.

Preprocessor stuff SHOULD be as fast as writing a literal in the place, the whole POINT of a preprocessor is that it alters the source before the compiler sees it. If variables are really faster why are there so many literals in your code? Are you sure your measurements weren't faulty?

Also why do you keep the walk thread alive when the unit is standing still, possibly adding a delay before it starts walking because of the sleep 500? I just make the while loop end when the walk ends and put the code to go into the idle stance below the loop. Nemo (IIRC) found that keeping a walk thread alive when it's not used causes a relatively big performance hit.
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Re: Cyclic Animation for Newbies

Post by zwzsg »

Some stuff really should be considered too simple to have such a restrictive license as GPL tied to it. Especially considering how the if(bMoving) are reminiscent of Cavedog's scripts.

I think what he meant is that, if Scriptor was a good and efficient compiler, then it would notice when an operation involve only constant and pre-calculate the result in the preprocessing, before the compilation. However, Scriptor is not a professionnal tool, and will not calculate anything, it'll leave mutliplication of constants and such in the compiled COB that will have to be calculated each time that line of script is run. And according to Argh, reading a value from a var is faster than reading a constant.
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Re: Cyclic Animation for Newbies

Post by KDR_11k »

zwzsg wrote:And according to Argh, reading a value from a var is faster than reading a constant.
I'd assume you mean reading the multiplied value from a var is faster than putting the multiplication in a precompiler statement because otherwise I don't get why he's using literals throughout his function.
User avatar
smoth
Posts: 22309
Joined: 13 Jan 2005, 00:46

Post by smoth »

maybe we are missing an extra function he is using to get speed that inside of it he gets a value to speed up or slowdown the unit animation depending on the current speed(to counter speedmetal or water sliding/skating)? I have thought of doing something similar myself but I have not tried it to see if it would work.

Thank others for the e-drama argh, I had nothing to do with it and I think I missed half of it.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Here's a little more detail on the basic arguments of efficiency.

The very fastest way to transmit data through the byte-code interpreter is the method that UpSpring attempts to do: breaking all move / turns down to Spring's fundamental units of measure (radians and linear constants, both of which are floats).

Therefore, the fastest possible expression of a turn command isn't:

Code: Select all

turn Piece to x-axis <90> speed <90>;
Instead, the fastest method is this:

Code: Select all

turn Piece to x-axis 23040 speed 23040;
This removes all computational steps between reading the bytecode and executing the instructions. However, one runs into obvious issues with maintainability! If you're absolutely sure that you're never going to modify your sourcecode, or you just like using a calculator, then this is the best possible method.

The next-best possible method, as KDR is pointing out, is convert all literals to a static constant. I prefer to go ahead and eat the memory-access costs of not providing instant access to the constant, myself, because then I can't screw up my entire script's timing by a single replacement error. However, you could phrase things like this, for a good speed increase:

Code: Select all

turn Piece to x-axis 90 * 256 speed 90 * 5.5 * 256;
... where 5.5 is our constant controlling speed.

The method I'm using is even less efficient, but it's easy to maintain and deploy. I may eventually cut everything to the maximum efficiency possible, when the game's content is feature-frozen, but for now, I prefer a simpler structure:

Code: Select all

MoveScript()
{
turn Piece to x-axis <90> speed <90>*RUN_SPEED;
}

Create()
{
RUN_SPEED = 5.5
}
Putting the code that way works just as well as:

Code: Select all

#define RUN_SPEED 5.5

Run()
{
turn Piece to x-axis <90> speed <90>*RUN_SPEED;
}
Lastly... preprocessor stuff assumes that COBs could read other COB code, saving precious memory-access speed. So far as I can tell, Scriptor simply compiles all calls to an outside script within the final COB, without exception, and Spring doesn't allow scripts to read other COBs.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Oh, and lastly... I don't suppose any enterprising people out there might be willing to write something that would automatically convert all turn and move commands to maximum-efficiency scripts? If so, then it'd become quite practical to write the script, test the script, and then convert it automatically, so that the original could be saved.
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Post by zwzsg »

Argh wrote:Therefore, the fastest possible expression of a turn command isn't:

Code: Select all

turn Piece to x-axis <90> speed <90>;
Instead, the fastest method is this:

Code: Select all

turn Piece to x-axis 23040 speed 23040;
WTF! Both produce strictly identical cob!

Well, actually, <90> gets translated into 16380, not 23040, but I'll just assume you borked your angular constant.



Anyway, just like I suspected, you use all important sounding words such as "now it's time to derive the speeds" just to say you do a substraction (if you really wanted to use scientific terminology, it would be a ΔX, not a dx/dt (and you don't derive the speed, but merely the position to get the speed)) but you actually have very little idea of what you're speaking about.


With those two lines you just lost all credibility.
User avatar
rattle
Damned Developer
Posts: 8278
Joined: 01 Jun 2006, 13:15

Post by rattle »

<x> = x*1/65536*360 = x*65536/360 = x*182.0444~, therefore the angular constant should be 182.044449 in scriptor

I assume the modifier tags are preprocessed so they're not going to have any impact on processing speed in the end.
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Post by KDR_11k »

Argh wrote:The next-best possible method, as KDR is pointing out, is convert all literals to a static constant. I prefer to go ahead and eat the memory-access costs of not providing instant access to the constant, myself, because then I can't screw up my entire script's timing by a single replacement error. However, you could phrase things like this, for a good speed increase:

Code: Select all

turn Piece to x-axis 90 * 256 speed 90 * 5.5 * 256;
... where 5.5 is our constant controlling speed.
You're confused. A constant would be somethign you #define, a variable is something declared with static-var and a literal is when you write a number in the code.
User avatar
FLOZi
MC: Legacy & Spring 1944 Developer
Posts: 6241
Joined: 29 Apr 2005, 01:14

Post by FLOZi »

zwzsg wrote:
Argh wrote:Therefore, the fastest possible expression of a turn command isn't:

Code: Select all

turn Piece to x-axis <90> speed <90>;
Instead, the fastest method is this:

Code: Select all

turn Piece to x-axis 23040 speed 23040;
WTF! Both produce strictly identical cob!

Well, actually, <90> gets translated into 16380, not 23040, but I'll just assume you borked your angular constant.



Anyway, just like I suspected, you use all important sounding words such as "now it's time to derive the speeds" just to say you do a substraction (if you really wanted to use scientific terminology, it would be a ΔX, not a dx/dt (and you don't derive the speed, but merely the position to get the speed)) but you actually have very little idea of what you're speaking about.


With those two lines you just lost all credibility.
My thoughts exactly, and I'm even half drunk damnit. :|
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

So, basically, I should quit sharing? K.
User avatar
TheRegisteredOne
Posts: 398
Joined: 10 Dec 2005, 21:39

Post by TheRegisteredOne »

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;
      }
   }
} 
tro's GPL massively complex circular motion hereby released under GPL:

Code: Select all

//tro's GPL Very Complex Circular Animation Cycle Code
//All GPL code within this GPL example, and the GPL method described, in all its GPL ways, is hereby GPL'ily released under the GPL.
StartMovng()
{
        spin Part around x-axis speed <120>;
}
StopMovng()
{
        stop-spin Part around x-axis;
        turn Part to x-axis <0> speed <300>;
}
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Look, I made a math error during a ten-minute speed-typing run during my lunch break :roll: There's no call to be rude, people. The silver lining (at least, for me), is that I learned some very useful things by re-evaluating what I'd said.

Since you've chosen to be rude for giving you methods and code and being generally nice, though, I'm just going to give you hints, instead of a roadmap. Figure it out yourselves. Not to mention that I was going to give you guys a lot more (and more useful) source, and now I'm not. Nevermind that with what I showed you in the "simple" "easy" code-writing method described above, I have taken writing a complex cyclical walkscript from hours of hellish coding to a few minutes of working with UpSpring, doing some simple math, and voila, you're done! You guys basically missed the point of the exercise, which is saving labor-time- efficiency was not the goal, and I was quite clear about that...

1. All allocation of variables in a COB is not performed the same way in the COB's physical length and position. Imagine a tape-drive- that's a COB. The perfect COB would run linearly.

2. Some allocations are far more efficient in terms of byte-code length than others.

3. Operations within a COB that do not require a push from outside the block will run more efficiently than calls that require a push.

4. Every line requires an allocation to the stack. Stacks are linear in nature, and are not blocks of indeterminate length. I suspect that the stacks either have an artificial upper limit, per COB being executed, or one that is set by Spring's code to attempt to maintain sync. Z, are you listening now? Long, involved scripts with multiple checks, pushes and allocations = bad. Short and choppy, if "simplistic" = good. Cavedog's methods are looking less-stupid now, frankly, although I think that the increment/decrement timing of turn / speed is not terrible in efficiency, if it is completely synchronous, like in my example code. Because it always returns perfectly, without having to drop or calculate carry-over. Not to mention it's less likely to break due to stack problems.

Spring will frequently bork things that are long. I discovered this in NanoBlobs, and I said stuff about it, and everybody ignored me. I now know why it works like it does.

Here, since I'm pissed off with your general rudeness, people... try this script, if you're smart enough to build a test object, and you can see a proof:

Code: Select all

piece 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;

Cycle()
{
hide 1;
hide 2;
hide 3;
hide 4;
hide 5;
hide 6;
hide 7;
hide 8;
hide 9;
hide 10;
hide 11;
hide 12;
hide 13;
hide 14;
hide 15;
hide 16;
hide 17;
hide 18;
hide 20;
hide 21;
hide 22;
hide 23;
hide 24;
hide 25;
show 1;
show 2;
show 3;
show 4;
show 5;
show 6;
show 7;
show 8;
show 9;
show 10;
show 11;
show 12;
show 13;
show 14;
show 15;
show 16;
show 17;
show 18;
show 20;
show 21;
show 22;
show 23;
show 24;
show 25;
sleep 2000;
return;
}

Create()
{
start-script Cycle();
}
5. All movements within a section are less wasteful than movements from section to section.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Now I know why Cavedog used turn-now and move-now; it's the most computationally-efficient method possible. Especially since their compiler worked right.

I also know how we can turn this into a working theory for far more efficient animation systems within Spring. Puzzled? Still think I'm full of crap? Go check out the move-script for Arasword.bos, which is included with Scriptor. It's very, very illuminating. So much for Cavedog being stupid- they just used a better compiler, and were more aware of what they were actually doing.

Now, I just need to figure out if there's any way to automate this and use efficient JUMPs within a script. I think that I'll see if I can get JC to wrap his brain around this one...
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Post by KDR_11k »

I have a feeling the engine you're testing this on is entirely different from the engine Cavedog wrote the code for, in addition to being designed for exactly the code Cavedog produced.

Meh, we're in an age where people no longer use assembly because we have enough CPU cycles that we can waste them on clean code rather than a hacky mess that's fast but needs a complete rewrite to be changed so I'm not really worried about making my scripts that efficient, I'm going for a mod with ~30 units in play per side. Besides, complex operations are to be done in Lua now so COB is probably going to end up with the 90% of the code, not the 10%.
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Post by zwzsg »

Your intents are good, Argh, it's your content and method which aren't.
  • First, your GPL headers. I can understand what you're trying to do with that. You think that, by issuing GPLed scripts, people will reuse them, enhance them and then release as GPL again, thus initiating a virtuous cycle leading to creation of a vast pool of ever improving scripts that every modder would benefit from. But it doesn't work like that.
    • The GPL is a very restrictive license, in the sense that if you use a bit of GPL in your project, you've got to license the whole thing in GPL. But many mods are one-man works whose author want to keep ownership over. The definition of derivative work is quite muddy, but with a good lawyer you could argue that a whole mod would have to be released under GPL if it contains one of your GPL script. Which is not acceptable for most modders. Especially since "that incident between you and Fang" showed that you're keen on enforcing the GPL license.
    • We don't need the GPL license to share BOS againt modders. For years TA scripters have shared their code and helped each other without the need of any license header. Whether it's due to laziness or to a spirit of sharing has yet to be determinated, but it's common for TA modders to leave their bos file in their released work. And even when they don't, most script are simple enough that they can be decompiled from the COB with good result. Even when the BOS isn't included and can't be easily decompiled from the COB, I've never heard of any modder refusing to share his BOS code when asked kindly, to the contrary they'd be happy to see their code reused.

      So getting the source code of a BOS is no problem.

      Now, for reusing BOS of other in your mod: I've never heard of anyone (beside you) throwing a tantrum because their script was stolen. I've seen quite a few people getting angry when their model was reused without permission, but script, never. Anyway, when copying too much too complex script, you can just ask the original author, as I said about everybody I know who write bos script will be happy to share them.

      So reusing other people BOS caused no problem, before you started GPLing it.
    • The script you are hereby releasing under the GPL are
      • much too simple
      • much too close to the Cavedog scripts
      to be licensed. What you're doing is the equivalent of copyrighting the wheel. You're taking very simple scripts that have been commonplace for age, don't even rewrite them that much, then release under GPL and hunt down people who don't respect your licensing term. That sort of bullying just make you hated by modders. Even if your intent where to bring love and sharing amongst scripter, your way of doing it is so it brings the exact opposite. Maybe once you write actually innovative and very complex script, you can have some licensing header on them. But for script that even yourself agree are "Very Basic", don't. Especially if your goal is to have people reuse them. Even more when they're barely changed Cavedog script. C'mon! You didn't even took the time to rename your bMoving variable! Your "CyclicAnimation()" script is simply a Cavedog "MotionControl()" with the "walk()" code directly included instead of called.

      If you think your code is close to Cavedog script only because there isn't that many ways to code walkscript then:
      • You're wrong, if you're interested I could show you more variations in the way to code walkscripts.
      • If there's only one way to code animation, you should agree that it's not copyrightable.
      • Anyway some little details prove that it's undeniably based on Cavedog script.
    • Maybe you'll retort that the TA community was small and close knit, and based on questionnable legal ground to start with, but that for Spring, you're expecting large community and want to have more solid legal ground. I don't believe that's true, but if you do and are convinced a proper licensing is that important, then please use something much less restrictive, such as a non-copyleft Free Content license.
  • Your initial post make it sounds like you created that thread as some sort of tutorial. This is very fine, we have many aspiring modders around and a dire lack of tutorials. However, when you're teaching, you're supposed to know well your subject (and you're not).

    Moreover, closer examination show that you created that thread not as "tutorial for newbie", but as a "great discoveries I'll be so kind as to enlighten you with". With such premises, you'd better deliver something else than what modders knew for years.

    Even more, you're not claiming to solely teach us to write script, but to teach us to write ultra optimised script. Not just "ok, let's avoid making framerate drop to slideshow" kind of optimisation, but "let's suck every possible extra CPU cycle" kind of optimisation. To write code require some skill, to write optimised code much, much more, and to write perfectly optimised code even more. If you simply made a "scripts tutorial for newbies", I could have been more comprehensive with it being littered with errors. I'd still have corrected you because I don't like misconception and falsehood spreading, but I wouldn't have pointed and laugh. But here you claim to deal with optimisation as hard core as using local variable because they're faster to load, or replacing one multiplication with its result. Which makes you look like a fool when you then proceed with basing your theories on gross errors. By the way, if you want to optimise your code, you shouldn't rely on abstruse theory, but on actual, real, measure. Because when trying to optimise code, it's way too easy to lost hour trying to save a CPU cycle while forgetting something very wasteful lying next to it. So, first, measure and identify what needs to optimised, then optimise it. And measure, measure, measure. Theories alone doesn't make science, you need to draw theory from experiment, and test theory with experiment.
  • Now, more on your errors:
    • What struck me immediatly is how you a large chunk of your post deal about how
      turn Piece to x-axis <90> speed <90>;
      isn't as fast as:
      turn Piece to x-axis 23040 speed 23040;
      If you had just taken the time to verify a bit what you're advancing, you'd knew it's not. I'll guide you by the hand:
      • Open a new file explorer, right click in some white blank, create new file, name it Dummy1.bos.
      • Open Dummy1.bos with Scriptor. Type the following into it:

        Code: Select all

        piece p;
        Dummy()
        {
        	turn p to x-axis <90> speed <90>;
        }
      • In Scriptor, press Alt F7 and verify your angular constant is set to 182, like it should be.
      • Compile
      • Close Dummy1.bos and exit Scriptor.
      • In the file explorer, right click on Dummy1.bos, copy and paste, rename the copy into Dummy2.bos.
      • Open Dummy2.bos with Scriptor. Edit it to change the <90> into 16380. So that the content of Dummy2.bos now is:

        Code: Select all

        piece p;
        Dummy()
        {
        	turn p to x-axis 16380 speed 16380;
        }
      • Compile
      Now compare the two COB. Ok, sadly this step is a bit more complex, since you need a hex viewer to be able to see the content of cob. And not everybody is familiar with hexadecimal file viewer. A simpler but not as thorough check would be to compare the filesize of the file, with a right click, property. Make sure to read the filesize, no the "size taken on disk". If both are equally sized, down to the byte it wouldn't be too extravagant to assume neither is more complex than the other. Or maybe you could uncobble the COB back into bos, and see they are uncompiled into the same bos, which is a rather strong indication, if I may say, that the COB are the same. But still, do a comparaison with an hex editor if you can, it's more impressive when you realise both BOS output identical COB down to each byte. For info, here's what both COB are:
      Image


      This is not simply a little math error. This instead reveal a deep lack of understanding of how Scriptor preprocessing and compiliation goes. You dedicated a large part of your post to that one optimisation, and even suggested other people should write applications with the sole purpose of performing that one optimisation. Yet a very simple and easy to peform check would have reveal that the gain is null, not just negligible, but null, both BOS source lines outputing the very same COB object code.

      Such an unforgivable error simply remove all credibility of any other optimisation claims you make.
    • On the same line, you appear to be under the impression <90> gets translated into 23040. Assuming your Scriptor use standard setting, <90> would stand for 90├é┬░, and get translated into 16380. Whenever scriptor seen <smthg>, it multiply smthg by the angular constant set with Alt F7. It's customary to use a constant of 182, so that value between < > are angles in degree. That 90 you choose also sounds like an angle in degree. By the way, 90├é┬░ would be 16384. However, the angular constant which should be 182.04444.... gets rounded to 182 by Scriptor, which produce the small approximation of <90> translated into 16380 and not 16384.

      That error, on the other hand, could be a simple typo, attributed to having typed that post too fast during a lunch break. And it doesn't really impact your reason for posting that code, which was how replacing <90> with its raw value would be an foptimisation. Just, I thought it was funny that you made an additionnal glaring error in so few lines of code. If you were such an expert of compilitation and code optimisation, we could expect that you knew your power of two. Personnaly I'm so used to typing them in TA unit script I don't even have to think to remember values such as 45°=8192 90°=16384, 180°=32768, 360°=65536 (I often type the real value because I have an irrationnal fear of tiny approximations). So, even if it could be considered an irrelevant typo, it still show you're not very used to the subject you're lecturing us about.

      Further reading around those lines of yours and computing what you used show however the miswriting of 23040 and not just 16384 is not merely a typo, but reveal deeper misconception. First, it show that you thought the angular constant was 256. Which could be caused by three things:
      • By <90>, you meant 128├é┬░. Not very plausible, a 128├é┬░ angle has nothing remarkable.
      • You think that in cob object, angles are written in 1/92160 of degree. Which makes about no sense, that's a completly unusual units, and makes angle use 17 bits. That is two bytes and a eight of a byte. Which make no sense computer wise. No one in his sane mind would suppose anything to use such an arbitray angular unit.
      • Simple typo, you actually know the usual angular constant is 182, but were in a hurry and confused and used 256 instead.
      Well, I could give you the benefit of doubt and retain the last option.
    • However, the line just above that part read:
      "breaking all move / turns down to Spring's fundamental units of measure (radians and linear constants, both of which are floats)"
      Which contains TWO! fundamental errors, enonced in doctoral tone.
      - Spring's fundemental unit of angle measure is not radian, but 1/2^16 of full circle
      - Spring's COB fundamental unit of linear and angular measure aren't floats, but integers. Distance are 32 bits signed integers, for angles, only 16 bits are used, and signed or not don't really matter for angles anyway. When a script do some calculation with it, angles are 32 bit signed integer anyway since it's the only variable type BOS/COB knows anyway (even the "piece" type is just some subtype of integers).

      Anyone with even the most basic programming knowledge knows confusing integers and floats is an unforgivable error, (I mean, confusin integer with char, pointer, longer and shorter integer, is okay, but confusing int with floats...). And 1 radian is very different from 2pi/2^16 radians.
    • Some more skimming your long post about optimisation shows lines such as:

      Code: Select all

      Create()
      {
      	RUN_SPEED = 5.5
      }
      or

      Code: Select all

      turn Piece to x-axis 90 * 256 speed 90 * 5.5 * 256;
      Which again reveals deep misconception of how Scriptor and BOS script works.

      Because the only variable type TA & Spring unit scripts can handle, even for intermediary result, is 32 bit signed integer, anytime you write 5.5 it gets floored to 5.

      When you write 90 * 5.5 * 256, what it does is (90*5)/256 and your decimal are lost at every step.
      If you really want to have decimals, the only way is to use fractionnals, and to do the division last. For exemple:
      90*256*55/100.
      With all the multiplication at first, and all division last. But beware, if the result of all multiplication goes bigger than (2^31)-1, there will be an overflow and the end result won't be what you expect.
      (Incidentaly, here again you use 256 where you meant 182, which show it's more than an insignifiant forgetfulness.)

    Frankly, after finding such a high concentration of fundamental errors and misconception in so few lines of your posts, I can't be bothered detailing the rest of your posts. Because it already proves me that yes, you are full of crap, and behind your "I'll share my discoveries, I'll give you a lot more of my source" posture, behind your impressive sounding wording, there's nothing valuable.
    I suspect that the stacks either have an artificial upper limit, per COB being executed
    [...]
    Z, are you listening now?
    [...]
    Spring will frequently bork things that are long. I discovered this in NanoBlobs, and I said stuff about it, and everybody ignored me. I now know why it works like it does.
    I think I hit TA scripts limits, such as "can't have more than 8-12 threads running at once (never got around to precisely measure the number)", "can't have more than 64 or 128 or so (never measured precisely either) static-var", "can't have more than 128 pieces (this one I'm pretty sure isn't far off", before you even wrote you first Spring scripts. I know some of those limits didn't carry over to Spring (for instance, Spring seem to have much less issues with many function of a script being run at once), but never bothered to prod and test Spring's limit.

    It would be very useful to actually measure and note down those limits, with precision, that's for sure. Or get a dev tell us what's the maximum set at in source. :P But don't delude yourself into thinking you're the first pionneer to have hit script limits.

So, yeah, good intents you have, and I aren't saying you should quit sharing. Just, error riddled content, lulz and anger for your attempt at teaching us script optimisation when you don't even grok the basis of script compilation, angered lulz for your copyrighting and labelling "revolutionnary step forward" what are age old simple scripts.

That last point is a bit tricky, since scripting skills have a very wiiiiiiiide scale. Sometimes I'm even under the impression the vast majority of modder simply recopy Cavedog scripts, simply changing piecename and a couple values, but without any understanding of the code they use. I mean, I've seen people I thought were skilled argue me about that walkscript wouldn't work anymore when stripped of their if(TRUE). And Cavedog scripts themselves are full of needless complication and bad methods. So, well, that you are actually understanding a bit of the scripting language, and trying to understand it more instead of the "my headsplode, plz write script for me" attitude too many modders have, is actually good new. But then, you've got to realise that during the several years TA modding community has been active, there's also been a few individuals who knew how to write script. It happened quite a few times that I went amazed at what I find when uncobbling units of ancient scripting masters, such as Brother Alpha. More recently, people like TheRegisteredOne, KDR_11k, Zodius, Boogie, show amazing talents and understanding at scripting, without feeling the need to post "Ultra Optimised breaktrough code released under GPL" all over the place, they know the forum crowd doesn't care about lines of codes but just the result, and other modders would simply look at their mod to see how it's done, or ask them. And many modders, while not making super innovative scripts, still know enough to get the job done and could teach you a few things. Well, what I'm trying to say is that the scripting skill scale is vast, and while if you look down you sure are impressed by the distance, if you could look up you'll see you aren't arrived yet.

So, in sort, keep sharing scripts, keep explaining and teaching scripting. But avoid GPL license, avoid touting as revolutionnary what is not, and avoid taking the posture of a lecturing expert on subjects you clearly don't master (such as compilation optimisation). Also, when dealing with topic you aren't fully accustommed to, testing your theory before posting helps alot.
User avatar
Boirunner
Kernel Panic Co-Developer
Posts: 811
Joined: 05 Feb 2007, 14:24

Post by Boirunner »

User avatar
rcdraco
Posts: 781
Joined: 22 Nov 2006, 02:50

GEEZ

Post by rcdraco »

This is insane, call the Nerd police.

But serious Argh, why can't you use a Custom license. Such as my personal favorite:
You may use, and feel free to modify this program under the following guidelines.

1. You may use this file/program free of charge, and may not make any profit from it whatsoever.

2. You may modify this program under the guidelines that if it contains any of my (the creator's) content that you give credit in a text file where it is easily legible for anyone to see if they want to use this file, and include it will all distributations of it. If it contains only your content or others content you need only follow the directions of their license if any.

3. I do not guarantee any success with the use of this program, use it at your own risk, but feel free to report any issues directly to me (the creator).

Have fun and happy modding!
User avatar
smoth
Posts: 22309
Joined: 13 Jan 2005, 00:46

Post by smoth »

zwzsg wrote: [*]We don't need the GPL license to share BOS againt modders. For years TA scripters have shared their code and helped each other without the need of any license header. Whether it's due to laziness or to a spirit of sharing has yet to be determinated, but it's common for TA modders to leave their bos file in their released work. And even when they don't, most script are simple enough that they can be decompiled from the COB with good result. Even when the BOS isn't included and can't be easily decompiled from the COB, I've never heard of any modder refusing to share his BOS code when asked kindly, to the contrary they'd be happy to see their code reused.
To me this is one of the most important points in that entire post.
Post Reply

Return to “Game Development”