Cyclic Animation for Newbies
Moderator: Moderators
Oh. Basically, I was just talking about what the final, compiled code looks like. What I was trying to get at was that:
1. You want as little pushed / fetched as possible
2. You want as few math steps as possible
So, "turn Part to x-axis <W> * X speed <Y> * Z" is a very stupid way to write code, because even if you're using local-vars in that expression, it's lousy with extra math steps.
However, when doing an initial setup, it's nice to do something like:
Just for ease of maintenance. Because you can always just go back with your calculator and manually remove the * speed later on, once you have tested the animation in Spring to make sure that it fits the gameplay you have in mind. All of the numbers will get converted to integers by the compiler.
What KDR is rightly pointing out is that if you maintain a bunch of things that all share the same Piece names, you could save a bunch of time by using #includes, so that everything shared a common codebase and did not have to be re-written from scratch. Which is great, if I was doing all human walkscripts, and I'll probably adapt that to my human walkscript that is shared in common with a lot of stuff in the game, actually. It's a good idea, if you have 10 things that are all basically the same, with minor differences. More work than it's worth, though, if you are talking about 10 different things with dis-similar setups, imo.
1. You want as little pushed / fetched as possible
2. You want as few math steps as possible
So, "turn Part to x-axis <W> * X speed <Y> * Z" is a very stupid way to write code, because even if you're using local-vars in that expression, it's lousy with extra math steps.
However, when doing an initial setup, it's nice to do something like:
Code: Select all
TestMe(Myspeed)
{
Myspeed = 5;
if(bMoving)
{
turn Part to x-axis <45> speed <15> * Myspeed;
}
}
What KDR is rightly pointing out is that if you maintain a bunch of things that all share the same Piece names, you could save a bunch of time by using #includes, so that everything shared a common codebase and did not have to be re-written from scratch. Which is great, if I was doing all human walkscripts, and I'll probably adapt that to my human walkscript that is shared in common with a lot of stuff in the game, actually. It's a good idea, if you have 10 things that are all basically the same, with minor differences. More work than it's worth, though, if you are talking about 10 different things with dis-similar setups, imo.
Last edited by Argh on 04 Jun 2007, 01:35, edited 1 time in total.
I've tested KDR's approach, and I ran into a giant, ugly problem: there's no way to prevent speeds from being returned as negative numbers, which always causes Spring to take the longest possible distance. This is not a problem, if you're content with a two-position walkscript, but it becomes a giant pain in the butt very quickly, as you see more and more areas where a single math error ruins your script.
There seems to be no way around this, that I can see, because everything in a #define needs to be a non-logic step- it doesn't understand if() statements, which is a necessary step here, because we have to evaluate the results, and there aren't any handy C/C++ operators like unsigned-ints that can prevent this.
So, if anybody has some way to evaluate the contents of a #define and preclude negative speeds being introduced, I'd like to hear about it- the only ways that I can see doing this include really ugly hack-arounds that are ultimately less-efficient than what I was doing previously.
There seems to be no way around this, that I can see, because everything in a #define needs to be a non-logic step- it doesn't understand if() statements, which is a necessary step here, because we have to evaluate the results, and there aren't any handy C/C++ operators like unsigned-ints that can prevent this.
So, if anybody has some way to evaluate the contents of a #define and preclude negative speeds being introduced, I'd like to hear about it- the only ways that I can see doing this include really ugly hack-arounds that are ultimately less-efficient than what I was doing previously.
- TheRegisteredOne
- Posts: 398
- Joined: 10 Dec 2005, 21:39
I don't think the compiler does that, "Myspeed" is a variable, it can change through the execution of the script, (you can pass in 5, 3, or anything into "Myspeed"), thus i think the compiler will leave something like "<15> * Myspeed; " inAll of the numbers will get converted to integers by the compiler.
Yup, you're right, if you leave it as MySpeed, sorry that wasn't at all clear.
If you #define MySpeed, then it gets turned into a literal. If you var MySpeed, it is treated much like a static-var, but it doesn't go outside the script loop looking for it, and will pick up the value again during the pass through the script... or never again, if defined before the While(TRUE), if I'm reading the output right.
However, that still leaves a multiplier step in there, so it's not, theoretically-speaking, very efficient. Having tested that tonight in a practical sense, though... so far as I can tell, that works quite a bit more efficiently than the previous way I was doing this, where I was using static-vars, and I'm rather tempted to just use this everywhere, because it makes writing a script so easy. I can always come back later and clean it up for pure optimization purposes later on, when I don't have five billion other things that need doing.
If you #define MySpeed, then it gets turned into a literal. If you var MySpeed, it is treated much like a static-var, but it doesn't go outside the script loop looking for it, and will pick up the value again during the pass through the script... or never again, if defined before the While(TRUE), if I'm reading the output right.
However, that still leaves a multiplier step in there, so it's not, theoretically-speaking, very efficient. Having tested that tonight in a practical sense, though... so far as I can tell, that works quite a bit more efficiently than the previous way I was doing this, where I was using static-vars, and I'm rather tempted to just use this everywhere, because it makes writing a script so easy. I can always come back later and clean it up for pure optimization purposes later on, when I don't have five billion other things that need doing.
- TheRegisteredOne
- Posts: 398
- Joined: 10 Dec 2005, 21:39
it is still the script that does the calculations, not the compiler. if you define "Myspeed" as 2, and compile "<30> * Myspeed", the compiler converts that to "<30> * 2" and that's it. you can try it. when you decompile the resulting cob, you get something like "<30> * <0.010989>", which is basically "<30> * 2".
Um, what do you have to do to cause negative speeds?
Also precompiler statements are pretty useful for code that gets reused with small variations, even if those variations are the number of variables used.
Example, an ammo script:
I can use that script for one ammo type by #defining only MAX_AMMO, I can use it for two weapons with #define MAX_SECONDARY_AMMO and I can use a custom condition to determine when a refill is needed by #defining CUSTOM_CONDITION to be what I need. Might be useful for the Advance Wars mod, they could use fuel through the same system.
Example use for a unit that will change its weapon according to its activation status upon refill:
(all code in this post available for free use)
Also precompiler statements are pretty useful for code that gets reused with small variations, even if those variations are the number of variables used.
Example, an ammo script:
Code: Select all
//
// Ammunition refill handler script
// Make sure your unit has a function called RefillAmmo()
//
#include "exptype.h"
#define IS_AMMOREFILLER h == H_MAIN_FACTORY
#define AMMO_REFILL_RADIUS [260]
// Let's assume that all units refill at the same range
// This is where the heights of all refillers would be listed, remember: UNIT_HEIGHT = s3o radius * 65536
#define H_MAIN_FACTORY 3342336 // 51 * 65536
static-var ammunition;
#ifdef MAX_SECONDARY_AMMO
static-var secondary_ammo;
#endif
CheckForRefills()
{
while(1) {
sleep 500; //short sleep if ammo is full
var uid;
var cnt;
cnt=0;
#ifndef CUSTOM_CONDITION
#ifndef MAX_SECONDARY_AMMO
if (ammunition < MAX_AMMO) { // don't check if already full
#else
if (ammunition < MAX_AMMO || secondary_ammo < MAX_SECONDARY_AMMO) { // don't check if already full
#endif
#else
if (CUSTOM_CONDITION) {
#endif
for (uid=get 70;uid >= 0;--uid) { //thanks, zwzsg
// get 70 = get MAX_ID
// for all units,
if (get 74(uid)) { // UNIT_ALLIED | that are allies,
var h;
h=get UNIT_HEIGHT(uid);
if (IS_AMMOREFILLER) { // and are ammo supplies
if (get XZ_HYPOT(get UNIT_XZ(uid) - get PIECE_XZ(body)) < AMMO_REFILL_RADIUS)
// see, if they are closer than AMMO_REFILL_RADIUS
{
ammunition = MAX_AMMO;
#ifdef MAX_SECONDARY_AMMO
secondary_ammo = MAX_SECONDARY_AMMO;
#endif
start-script RefillAmmo();
uid = -1; //break the loop since ammo is already full
}
}
}
++cnt;
if (cnt>50) {
sleep 30; //do the checks spread out over multiple frames
cnt=0;
}
}
}
}
}
Example use for a unit that will change its weapon according to its activation status upon refill:
Code: Select all
RefillAmmo()
{
hide ammosymbol;
weapontype = get ACTIVATION;
if (weapontype) {
hide rifle;
show missilelauncher;
}
else {
show rifle;
hide missilelauncher;
}
}
#define MAX_AMMO 50
#define CUSTOM_CONDITION ammunition < MAX_AMMO || weapontype != get ACTIVATION
#include "ammunition.h"