Aircraft: steering code reliability

Aircraft: steering code reliability

Discuss the source code and development of Spring Engine in general from a technical point of view. Patches go here too.

Moderator: Moderators

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

Aircraft: steering code reliability

Post by Argh »

Ok, I've taken a detailed look at aircraft steering code, and I've found a lot of problems.

Very simply put, aircraft don't obey their steering limits during a lot of behaviors. This results in inconsistent flight behaviors and responses, and can very easily lead to borked flight patterns.

Let's look at an aircraft with a custom setup:

Code: Select all

	TurnRate=1800;

	WingDrag=0.14; //drag caused by wings
	WingAngle=0.16; //angle between front and the wing plane
	Drag=0.01; //how fast the aircraft loses speed (see also below)
	FrontToSpeed=0.2; //fudge factor for lining up speed and front of plane
	SpeedToFront=0.14; //fudge factor for lining up speed and front of plane
	MyGravity=0.2; //planes are slower than real airplanes so lower gravity to compensate

	MaxBank=0.2; //max roll
	MaxPitch=0.2; //max pitch this plane tries to keep
	TurnRadius=200; //hint to the ai about how large turn radius this plane needs

	MaxAileron=0.015; //turn speed around roll axis
	MaxElevator=0.08; //turn speed around pitch axis
	MaxRudder=0.02; //turn speed around yaw axis 
This code basically causes two (mutually-exclusive) behaviors to happen:

1. During "normal flight", the aircraft obeys all the specifics as to flight parameters.

2. During "combat flight", the aircraft suddenly uses custom parameters tied to the value of TurnRate.

This is 99% of what's wrong with aircraft, and getting them to behave in a predictable fashion. The code I've written, above, results in an aircraft with a turn-radius of approximately 200 Spring units. However, whenever it detects an enemy, TurnRate overrides these behaviors, causing the aircraft to suddenly perform ridiculously tight loops and microscopic spins.

Spring devs... please, for the love of Dog, this needs to get fixed. Now that I know what's happening... it's easily repaired. Just have the code check for custom flight parameters- if any of the values aren't the defaults, then the aircraft should use the custom parameters at all times, not refer to TurnRate and suddenly use a bunch of defaults multiplied, no doubt, by TurnRate.

The only times TurnRate should matter are in the following situations, I think:

1. Since there are no variables that allow modders to define HoverAttack properties, TurnRate should define the maximum spin-rate around the Y axis of the model during HoverAttack, in radians/second.

2. It should be used to determine attack parameters if the Unit doesn't have any of the specific parameters defined in the FBI.

That's what's ultimately borked about aircraft- the steering code is taking control and wresting it away from the designers when it feels it's appropriate, with results that aren't very good. Making this change should be very simple, and backwards-compatible with existing mods, but allow for future mods with much more reliable aircraft behaviors.



The last thing that's wrong with aircraft is a specific issue and a special case. Aircraft aren't "looking ahead" far enough to avoid collisions with the ground or other aircraft. This isn't a huge problem for high-flying aircraft, but for anything that is meant to hug the terrain, it can cause a lot of problems. I do not know how aircraft "see" their world, but it's pretty obvious after playing around with the code for awhile that they don't anticipate collisions correctly, and frequently understeer when approaching objects at speed, only to obviously (and somewhat comically) "see" the oncoming object and then oversteer while trying to avoid it. I assume that aircraft "see" the terrain heightmap and try to plot paths that avoids impact, but they do not seem to look far enough ahead, even at low velocities.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

In addition, I've found one additional problem, which is ... erm... fairly major...

If the value of TurnRate is lower than the values supplied by customized flight parameters, the aircraft will not steer at all. It will just fly in a straight line after being built... forever :P

I'd call that a bug.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Yikes. Aircraft with custom values fairly frequently fly off the battlefield, and are never told to turn back- they just fly off the field... forever :|

Methinks this may have to do with some borked stuff having to do with the new code for waypoints, but I dunno. There has to be some way to have the aircraft assigned waypoints to the side but heading back to the battlefield, so that they will come back onto the field every time :P
User avatar
LordMatt
Posts: 3393
Joined: 15 May 2005, 04:26

Post by LordMatt »

A relevant post in the BA thread about stealth fighters and this problem.
User avatar
Fanger
Expand & Exterminate Developer
Posts: 1509
Joined: 22 Nov 2005, 22:58

Post by Fanger »

Get rid of those drag values, they are WAY to high, high drag values cause the aircraft to speed up as the larger the number the less drag the aircraft seems to sustain leading me to believe that those are multipliers of a certain speed..

The Turn radius value doesnt actually affect how the aircraft turns in anyway, what does affect it are the rudder, aileron, and elevator values.. these will overide the turnrate value when added and specify how the aircraft will turn around each individual axis..

In addition speedtofront and fronttospeed will alter how the aircraft turns by allowing it to effectively slide around turns as the nose does not update to the new direction fast enough/the aircraft accelerates in a new direction before being straightened out..

Best explaination for these values Ive found so far is this excerpt from a post by Mad Rat, he was fairly on the money with most of his hypothisizing..
Drag= same as brakes but for planes (0.0 to 1.0)

WingDrag= braking effects caused by a change of direction, mimicks wing effects (0.0 to 1.0)

WingAngle= basically how much of a turn before WingDrag kicks in (0.0 to 1.0)

From the descriptions I'd make the following guesses:

maxBank= cosmetic look of a turn

maxPitch= cosmetic look for climb, has no impact on maximum climb rate

turnRadius= educated programmer's guess for ai planning, but has no real
control of turn rate

myGravity= basically control of its climb rate

maxAileron= maximum roll rate (pivots on axis front to back, controls roll clockwise and counterclockwise)

maxElevator= maximum pitch rate (pivots on axis side to side, controls nose up and down)

maxRudder= maximum yaw rate (pivots on axis top to bottom, controls nose right and left)

frontToSpeed= how far nose can be offline before accelleration(?)

speedToFront= rate for the nose to align to new (true) direction change(?)
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Hey, I found an interesting mystery bug... I think that aircraft aren't steering right if they're over water, in some conditions... I think that maybe they're not reading their height correctly...

This bug is causing them to fly in absolutely straight lines, forever, if they are over water or any other part of the map that is at 0 height (say, if they fly off the edge of the map)... it's what's causing the "flying off to infinity" bug in my current project...
User avatar
Fanger
Expand & Exterminate Developer
Posts: 1509
Joined: 22 Nov 2005, 22:58

Post by Fanger »

I still think you should post the exact values your using so other people can check on your findings...
User avatar
MadRat
Posts: 532
Joined: 24 Oct 2006, 13:45

Post by MadRat »

I once tried to write a missile trajectory program in BASIC. One of my biggest problems was understanding the effects of using directions based on 0-360 degree headings. What would happen is if I compensated for sub zero or beyond 360 degree headings then all sorts of strange behaviors would happen. I wanna say the solution was to use the absolute value of each variable involved before making any adjustment, which eliminated problems with negative co-efficients. Just brain storming what the problem could be. Otherwise disregard my uninformed commentary. :)
User avatar
ILMTitan
Spring Developer
Posts: 410
Joined: 13 Nov 2004, 08:35

Post by ILMTitan »

heading = heading % 360

edit: where % is the modulus operator
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

After more experimentation, here are my results:

1. No matter what Acceleration and BrakeRate are set to, aircraft do not obey them! They obey Drag for slowing down purposes, but they all accelerate at the same rate!

2. No matter what I do in terms of making turn-rates faster, aircraft that miss a Waypoint are in severe danger of being handed some sort of crap value for their next Waypoint, and frequently exit the map entirely. I have been able to make this happen with the default values as well standard values. Clearly, Aircraft are not updating their Waypoints properly if they miss one :|

3. Aircraft are unbelievably stupid about how to handle "I can't land there because someone is already blocking the ground" problems. Instead of searching reliably for the nearest valid landing-point, they frequently revert to "I've missed a Waypoint" behaviors, leading to behavior no. 2, above :(

4. Lots of custom values result in really irrational behaviors, probably due to scaling factors interacting in strange ways. For example, setting the FrontToSpeed and SpeedToFront values above 0.3 or so frequently results in aircraft that shoot straight up at speeds that exceed their MaxVelocity, which should never happen.

5. Aircraft are not using the centers of the Radii as the centroids for rotation. They are using the model's Origin. This is just plain wrong.



Basically... the aircraft steering code needs to get rebuilt. SJ built something that is too much of a sim, and not nearly reliable enough in practice. With the new, more forgiving waypoint code, which works great for ground units... aircraft are doing some really screwy things, but the problem is a lot deeper than that. Here are the things I see:

1. Aircraft simply aren't reliably dealing with terrain. Their lookahead is poor, and they frequently don't steer correctly. The code deals with this by allowing aircraft to clip through obstacles, which is obviously less than ideal. After viewing that, I am of the opinion that aircraft which have a CruiseAlt value lower than 255 (yes, 255, the maximum Spring terrain height) need to use the full steering code for ground vehicles. They really need to see the whole terrain and steer around obstacles reliably- right now, they ignore Features and other Units, unless they are fellow Aircraft, or the Aircraft is attempting to land.

2. Aircraft which fly above the max. range of their weapons exhibit bizarre targetting behaviors, indicating clearly that they are not searching for targets in all three dimensions like they should be. Set an aircraft's CruiseAlt to 1000, and you will watch it dive-bomb targets that it cannot hit, firing the whole way down. This is really counterintuitive behavior.



If the problems with aircraft steering completely off the map were solved, I could live with most of the other weird things they do, but most of us trying to design games are even less happy with this code's performance than I am.

I took a look at the code. I think that the non-landing behaviors are the result of aircraft leaving DIRECT_CONTROL and being passed to EndNormalControl(), where I suspect that they're getting stuck in a loop that never passes them back to user commands.

There are a lot've other things that are borked, or don't make sense. Looking at the aircraft code, I get the feeling that there's about twice as many lines as it actually needs- maybe I'm just stupid to see the necessity, but it seems awfully messy, and there are big chunks that have been commented out with no comments as to why. The biggest issue I can see in the code is that while aircraft try to "reserve" a landing spot, they're just reserving a point- they're not reserving a full grid. This is a big problem, because one aircraft is fine... multiple aircraft attempting to land are likely to cause problems... and the code doesn't seem to handle this at all :P
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Oh, yeah, and here are more variables to play with. Some should do something, but get overridden (the variable MaxDrift, in particular, could be used to solve the landing problems, if it wasn't being overridden later), some weren't documented in the Wiki and work just fine (maxAccel, maxSpeed)...

I should note that speedtoFront and fronttoSpeed are both terrible, nasty hacks that shouldn't be in the code to start with, and that turnRadius is completely worthless (I don't think it's really doing anything, based on my reading of how the code is actually doing steering) and I see no point in setting MaxDrift/Pitch to less than 1.0 until the code actually OBEYS that limitation, since it has a nasty tendency to rotate aircraft far faster than their parameters allow. Also I should note that maxAccel should be kept low, since it is applied during the middle of turning behavior, instead of being applied after turns have completed, ala real aircraft... it would've been soooo much smarter to just assume that nobody in the Spring universe would turn below their aircraft's stall speeds, and apply acceleration values only at the end of turns, with a minimum speed value reached after N frames of turn, but nobody asked me when this was written, hehe...

Anyhow, here ya go, for those of you who want to fiddle. Me... I'm going go do something else now...

CruiseAlt=10;
floater=0;
canSubmerge=0;

isFighter=1;
loopbackAttack=1;
wingDrag=0.01; //drag caused by wings
wingAngle=0.01; //angle between front and the wing plane
frontToSpeed=0.01; //fudge factor for lining up speed and front of plane
speedToFront=0.01; //fudge factor for lining up speed and front of plane
myGravity=0.2; //planes are slower than real airplanes so lower gravity to compensate

maxDrift=256;
maxBank=1; //max roll
maxPitch=1; //max pitch this plane tries to keep

turnRadius=100; //hint to the ai about how large turn radius this plane needs
wantedHeight=10;

maxSpeed=5;
maxAcc=0.25;
maxBreaking=0.05;
maxAileron=0.01; //turn speed around roll axis
maxElevator=0.01; //turn speed around pitch axis
maxRudder=0.03; //turn speed around yaw axis
inefficientAttackTime=100;
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Ok, one last round of ranting, then I'm going to take a nap (been working on explosion FX too long this weekend, haven't slept yet):

I think that this code needs to be re-written from start. Here's how I think it should work:

Aircraft should have the following variables to control their behaviors:

maxSpeed
minSpeed
maxAccel (only used during straight-line flight)
maxAltitude (fiat- AI will not fly a path higher than this, overrides other steering code)
minAltitude (same as maxAltitude)
searchArea (to determine where valid landing spots are)
maxturnY (float, multiplied by 360*256 to get radians/slowUpdate- 360 means it spins a full 360 degrees in one slowUpdate)
maxaccelturnY (how quickly the turn reaches maxturnY)
maxturnX
maxaccelturnX
maxturnZ
maxaccelturnZ
turnDecel (amount of deceleration applied during turns on the Y axis, down to minSpeed)
climbDecel (same as turnDecel, but applies to turns on the X axis)
diveAccel (if the plane is heading down, how much speed do we add?)

That's all we actually *need*. The rest is bells and whistles. Here are my suggestions:

attackBehavior (this is special, see notes below)
attackbehaviorDelay(timed delay after attack before invoking an attackBehavior)
defenseBehavior(ditto)
defensebehaviorDelay(ditto)
airpadsearchRadius(float, search field for the aircraft to find valid pads)
fuel
bombingRunLength(special, for things we want to do logical ground-attack behaviors)

For attackBehavior, I'd have to suggest something a lot more useful than the current code, which has canloopbackAttack and that's all.

What I'd like is a system that calls up behaviors randomly from a list, that would look like this:

attackBehavior=Loop,breakRight,breakLeft,breakRight;

This would result in the following behavior:

On the initial run, if the Aircraft has a valid Target, it simply attacks and leaves the area, waiting the number of frames specified in attackbehaviorDelay.

Then it picks out an attackBehavior from the list of available options. People could stack them to have the aircraft pick an option more often, or just not define it, in which case the behavior picked would be random. The Behavior would then get called, and the aircraft would do the following:

Loop: Aircraft turns on the X axis, using maxturnX and maxturnaccelX, until it faces the target again, then stops turning on X, spins 180 degrees on the Z axis at maxturnZ and is locked into a state called AttackRun until it has passed the target, whereupon the attackbehaviorDelay cycle starts all over again.

breakRight/Left: Same as Loop, just using the Y axis, with some sort of basic math to make it bank and unbank as it attacks.

Waggle: randomly turns one to three times on the X or Y axis, then performs a breakRight/Left randomly. A way for aircraft to avoid AA fire or missiles and then attack again, basically.

diveBomb: aircraft turns on the Y axis and climbs, slowing due to climbDecel, until it reaches maxHeight. Then it picks a breakRight/Left randomly, and once facing the target, turns on its X axis to point at the target's center. It breaks upwards on the X axis to avoid running into the target, based on the height of the target and the distance/speed.

bombingRun: aircraft rises at 0.5 * maxturnY until it reaches maxHeight, then performs a breakRight/Left and passes over the target. Good for bombers, who need to go far enough away and high enough up that they can get their whole run in.


Defensive behaviors are invoked every time an Aircraft is not currently locked into an AttackRun state and has taken damage. I don't think some sort've fancy "am I being targetted" code is really necessary, but Spring doesn't do *anything* right now- aircraft are basically just mindless drones, trying to attack each other whether it makes any sense or not. I have bombers in my mod that try to fricking dogfight because they're too stupid to "know" that they shouldn't bother, and other similar problems


Lastly... aircraft need to obey OnlyTargetCategories! They do not do this, and I reported this on Mantis as a bug, because it's basically screwing up some behaviors I'd like to see possible in Spring... :|
User avatar
Guessmyname
Posts: 3301
Joined: 28 Apr 2005, 21:07

Post by Guessmyname »

*me throws in his support for this*

Don't forget the code for airial transports and dropship behaviour

[quote="Argh"]been working on explosion FX too long this weekend, haven't slept yet]/quote] Argh, go to sleep. Now, before you collapse onto your keyboard
User avatar
1v0ry_k1ng
Posts: 4656
Joined: 10 Mar 2006, 10:24

Post by 1v0ry_k1ng »

dive bomb would be cool
j5mello
Posts: 1189
Joined: 26 Aug 2005, 05:40

Post by j5mello »

stukas anyone?
User avatar
MadRat
Posts: 532
Joined: 24 Oct 2006, 13:45

Post by MadRat »

It seems like an awfully big task to rewrite it all from scratch at this point in time. It would probably help the devs, argh, if you could limit the issues at hand. I really like your idea about pathfinding below 255. I think it needs to go one step further, though, and aircraft need to fly untethered to the ground form above 255.

The accelleration issue is something that is probably fixable with little effort. It probably got overlooked in air units' infancy. I've always though the accelleration needs to be factored into fighting gravity when units climb altitude. As it is now the units apply drag when they reach different parameters, but otherwise have limitless thrust for uber accelleration within those parameters.
User avatar
MadRat
Posts: 532
Joined: 24 Oct 2006, 13:45

Post by MadRat »

I was thinking of the factors that go into steering real planes. What is missing in this game is "cornering velocity"; the optimal velocity for best manuevering. The steering should be less optimal at high speeds due to wider turning arc, and less optimal at lower speeds because it would theoretically lose either its lift or its forward momentum more so than it built up in potential energy.

A definitive "maximum altitude" would also help, basically an altitude where drag kicked and the aircraft eventually loses all forward momentum until it gets back down below that threshold. This kind of behavior would mimick the older tactic called "zooming", where an aircraft unable to sustain high altitude flight could still make a dead stick slash attack at a straight-line flying high altitude target. Instead of turning away after the attack, gravity and lack of lift would force the attacker to drop altitude upon reaching its apex. Its safe to say it wasn't real effective for killing targets.

The third and final idea that I'd like to see implemented is altitude effects on flight velocity. Maximum velocity should be halved when flight occurs at altitudes below 255 and scale exponentially to their full maximum velocity when they reach cruising altitude. Altitudes between maximum altitude and cruising altitude should again be sub-optimal and induce a slight speed penalty, too.
User avatar
Nemo
Spring 1944 Developer
Posts: 1376
Joined: 30 Jan 2005, 19:44

Post by Nemo »

j5mello wrote:stukas anyone?
...are already in '44 and divebombing away :D

They work rather well.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

More on this:

Aircraft with a MaxHeight and WantedHeight lower than about 30 frequently (about one in 4) get "lost" and refuse to accept any input from users. They then fly entirely off the map, seeking some point at the edge of the Spring world :P

This is a really big problem for me, in the mod I'm making- I have low-flying units that really need full flight dynamics for realistic behavior. I have a theory about what's happening... I'll do a test tonight/this morning when I get home (I'm pulling a 12-hour shift, yay) to determine if I can puzzle out what's going on. I think that there's some sort've offset in the code that's preventing them from seeing the terrain accurately, but I'm not sure.

Also, while I'm on this topic... we need a Boolean that shuts off the default "I'm dead, now what happens" code. The randomized "emit smoke, go into a spin, then eventually either blow up or hit the ground" code really sucks if you have to set myGravity low enough to allow for certain types of behavior, and it overrides the COB instructions. I am going to try to see if there are any workarounds for this.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

I have found out ways to solve a LOT of the current problems. I now know what's causing some of the borked behaviors.

First off, the turnRate of the Unit used when the unit is just trying to get from point A to point B. That's why setting that to zero or lower than a value of 1 results in locked flight behavior.

Secondly, the values for maxVelocity, Acceleratation, and brakeRate all need to be present, or the unit will never, ever land- it'll take off once, but then it won't return to the ground. For the most realistic behavior possible, you want these values to be identical to maxSpeed, maxAcc, maxBreaking (why Breaking, not Braking? lol).

Basically, setting maxAileron/Elevator/Rudder so that it doesn't match turnRate results in less than ideal behavior. TurnRate = 0.16 * TurnRate = degrees / second, so a TurnRate of 100 = 16 degrees turn/second, which if we break that down to radians and express as a float, should be (256 * 16 / 65536 = 0.0625). I think that's the right math there...

Using values that are not all even results in fairly crappy behavior. The value for pitch vs. rudder is very important- roll is mainly cosmetic, although the code *is* waiting for the unit to reach the desired rotation, so if speeds are out've sync, it can lock, resulting in bizarre behaviors.

Also, I found that the value maxHeightDif is really, really important, and I didn't even find that value until I looked in values being entered into UnitDef. It provides a lock that prevents the unit from routinely zooming below 0 height, which is one (of several) things that cause borked behaviors. Smoth, for your "flying things too high causes problems" issues, you might want to mess with that.

Lastly, frontToSpeed and speedToFront are super, super important if you're trying to go for certain behaviors. If you want "loose" steering, where the tail of the fighter "slides" around turns, then set this very low (below 0.01). For traditional, "tight" steering, where the aircraft acts like an aerofoil, set the value higher. I don't understand the interrelationships in the code entirely yet, but the value appears to be a multiplier to speed, used to fake the fighter's position where it "should" be, because whoever coded this (KMan? SJ?) didn't want to do a more straightforward simulation (why, I really don't know- the way I described above would have been more predictable and involve less mess, but meh) and so this "fudge factor" was used to fake the plane's positioning.

maxDrift doesn't seem to actually do anything. I can see what it's supposed to do, in the code, but I don't think it actually works right.

inefficientAttackTime is extremely interesting. Raising it causes the loop before the aircraft comes back to attack again... longer. Period! The actual distances, curves, etc., that the aircraft has to do are controlled by the maxAileron/etc. code during what I refer to as "combat flight behavior", which is very distinct from "go from A to B" behaviors. So, if you want to have a bomber that actually behaves realistically, you could have it have a very long inefficientAttackTime, and then it will not start turning until it's passed that distance. That was yet another little fudge-factor, but now that I know what it does... it's pretty useful.

myGravity is a lot less of a big deal than I initially thought. Now that I understand how things are working better... while "lift" is being calculated (a huge mistake, imo, but meh, I've already discussed what I'd have done) if you set wingDrag and wingAngle low enough, then myGravity ceases to be much of a factor in the overall simulation, because it acts in conjunction with the other two, and gets multiplied into near-zero values. It only really matters when the aircraft needs to fall out of the sky, basically... so, now that I know that, I can make things fall at a speed of 1.0, instead of a cartoony 0.6.

I will be posting up working, new flight parameter sets soon. It's harder than it might appear to juggle all of this stuff and end up with something that works, so a few examples might be very helpful, for people wanting truely custom flight behaviors.
Post Reply

Return to “Engine”