0.73b: Yup, more Weapons Stuff

0.73b: Yup, more Weapons Stuff

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

0.73b: Yup, more Weapons Stuff

Post by Argh »

Here are the current bugs / issues I'm having with Laser Weapons:

1. After extensive testing, I have determined that all non-model weapons (i.e., those that do not have a hitsphere of their own) are still not impacting on targets appropriately, and simply do not exhibit proper hit detection. I do not know whether this is because these weapons have an associated model or not, but I now strongly suspect this is the case, after performing additional tests.

This is a bug from 0.72b, and in 0.73b, it's still present and unfixed. This is a very serious bug, from a gameplay standpoint. Laser weapons are utterly useless against fast-moving or small targets and seemingly-solid beams often pass through smaller objects' hitspheres entirely... without generating a hit.

I don't know what causes this bug, but it has something to with the hitscan code, which apparantly treats these weapons as if they're points that are very tiny. I have a feeling there's a rounding bug or something simple like that going on, because these weapons work just fine on larger or slow-moving targets- just not small, fast ones.

If you want to see me actually use the majority of my fancy laser/plasma/etc. FX things that I'm building for the GPL version of NanoBlobs (and obviously would like to give to Spring's community)... erm, please fix this, it's probably just a teeny thing, with big consequences ;)

I'm not sure where this going wrong. I've looked at the rts\Sim\Misc\QuadField.cpp hitscan code, and I'm confused.

This section, if I had to guess, looks like it's the one being used for laser beams and other point objects:

Code: Select all

vector<int> CQuadField::GetQuadsOnRay(float3 start, float3 dir, float length)
{
	vector<int> quads;

	if(start.x<1){
		if(dir.x==0)
			dir.x=0.00001;
		start=start+dir*((1-start.x)/dir.x);
	}
	if(start.x>gs->mapx*SQUARE_SIZE-1){
		if(dir.x==0)
			dir.x=0.00001;
		start=start+dir*((gs->mapx*SQUARE_SIZE-1-start.x)/dir.x);
	}
	if(start.z<1){
		if(dir.z==0)
			dir.z=0.00001;
		start=start+dir*((1-start.z)/dir.z);
	}
	if(start.z>gs->mapy*SQUARE_SIZE-1){
		if(dir.z==0)
			dir.z=0.00001;
		start=start+dir*((gs->mapy*SQUARE_SIZE-1-start.z)/dir.z);
	}

	if(start.x<1){
		start.x=1;
	}
	if(start.x>gs->mapx*SQUARE_SIZE-1){
		start.x=gs->mapx*SQUARE_SIZE-1;
	}
	if(start.z<1){
		start.z=1;
	}
	if(start.z>gs->mapy*SQUARE_SIZE-1){
		start.z=gs->mapy*SQUARE_SIZE-1;
	}

	float3 to=start+dir*length;

	if(to.x<1){
		to=to-dir*((to.x-1)/dir.x);
	}
	if(to.x>gs->mapx*SQUARE_SIZE-1){
		to=to-dir*((to.x-gs->mapx*SQUARE_SIZE+1)/dir.x);
	}
	if(to.z<1){
		to=to-dir*((to.z-1)/dir.z);
	}
	if(to.z>gs->mapy*SQUARE_SIZE-1){
		to=to-dir*((to.z-gs->mapy*SQUARE_SIZE+1)/dir.z);
	}
	//these 4 shouldnt be needed but sometimes we seem to get strange enough values that rounding errors throw us outide the map
	if(to.x<1){
		to.x=1;
	}
	if(to.x>gs->mapx*SQUARE_SIZE-1){
		to.x=gs->mapx*SQUARE_SIZE-1;
	}
	if(to.z<1){
		to.z=1;
	}
	if(to.z>gs->mapy*SQUARE_SIZE-1){
		to.z=gs->mapy*SQUARE_SIZE-1;
	}
//	if(to.x<0){
///		info->AddLine("error %f %f %f %f %f %f %f %f",start.x,start.z,to.x,to.z,dir.x,dir.z,dir.y,length);
//	}

	float dx=to.x-start.x;
	float dz=to.z-start.z;
	float xp=start.x;
	float zp=start.z;
	float xn,zn;
	float invQuadSize=1.0/QUAD_SIZE;

	if((floor(start.x*invQuadSize)==floor(to.x*invQuadSize)) && (floor(start.z*invQuadSize)==floor(to.z*invQuadSize))){
		quads.push_back((int(start.x*invQuadSize))+(int(start.z*invQuadSize))*numQuadsX);
	} else if(floor(start.x*invQuadSize)==floor(to.x*invQuadSize)){
		int first=(int(start.x*invQuadSize))+(int(start.z*invQuadSize))*numQuadsX;
		int last=(int(to.x*invQuadSize))+(int(to.z*invQuadSize))*numQuadsX;
		if(dz>0)
			for(int a=first;a<=last;a+=numQuadsX)
				quads.push_back(a);
		else
			for(int a=first;a>=last;a-=numQuadsX)
				quads.push_back(a);
	} else if(floor(start.z*invQuadSize)==floor(to.z*invQuadSize)){
		int first=(int(start.x*invQuadSize))+(int(start.z*invQuadSize))*numQuadsX;
		int last=(int(to.x*invQuadSize))+(int(to.z*invQuadSize))*numQuadsX;
		if(dx>0)
			for(int a=first;a<=last;a++)
				quads.push_back(a);
		else
			for(int a=first;a>=last;a--)
				quads.push_back(a);
	} else {
		bool keepgoing=true;
		while(keepgoing){
			quads.push_back((int(zp*invQuadSize))*numQuadsX+(int(xp*invQuadSize)));

			if(dx>0){
				xn=(floor(xp*invQuadSize)*QUAD_SIZE+QUAD_SIZE-xp)/dx;
			} else {
				xn=(floor(xp*invQuadSize)*QUAD_SIZE-xp)/dx;
			}
			if(dz>0){
				zn=(floor(zp*invQuadSize)*QUAD_SIZE+QUAD_SIZE-zp)/dz;
			} else {
				zn=(floor(zp*invQuadSize)*QUAD_SIZE-zp)/dz;
			}
			
			if(xn<zn){
				xp+=(xn+0.0001)*dx;
				zp+=(xn+0.0001)*dz;
			} else {
				xp+=(zn+0.0001)*dx;
				zp+=(zn+0.0001)*dz;
			}
			keepgoing=fabs(xp-start.x)<fabs(to.x-start.x) && fabs(zp-start.z)<fabs(to.z-start.z);

		}
	}

	return quads;
}


If I'm reading that code right, it uses a ridiculously low value XYZ for the size of these objects. This may be causing it to round to zero on later steps. I dunno... I'm not really sure if this is even the right code.

This sections below states it's for "projectile collisions". Does that mean nuke/antinuke, or for weapons in general, or... what? If anybody can point me to the code that actually handles BeamWeapon/BeamLaser collisions with objects, so I'm not just groping around here, I'll take a longer look at this... as I said, though, this is probably a very quick fix.

Code: Select all

// optimization specifically for projectile collisions
void CQuadField::GetUnitsAndFeaturesExact(const float3& pos, float radius, const vector<int>& quads, vector<CUnit*>& dstunits, vector<CFeature*>& dstfeatures)
{
	int tempNum=gs->tempNum++;

	vector<int>::const_iterator qi;
	for(qi=quads.begin();qi!=quads.end();++qi){
		list<CUnit*>::iterator ui;
		for(ui=baseQuads[*qi].units.begin();ui!=baseQuads[*qi].units.end();++ui){
			if((*ui)->tempNum!=tempNum){
				(*ui)->tempNum=tempNum;
				dstunits.push_back(*ui);
			}
		}
	}

	for(qi=quads.begin();qi!=quads.end();++qi){
		list<CFeature*>::iterator fi;
		for(fi=baseQuads[*qi].features.begin();fi!=baseQuads[*qi].features.end();++fi){
			float totRad=radius+(*fi)->radius;
			if((*fi)->tempNum!=tempNum && (pos-(*fi)->midPos).SqLength()<totRad*totRad){
				(*fi)->tempNum=tempNum;
				dstfeatures.push_back(*fi);
			}
		}
	}
}


2. I have yet to get LargeBeamWeapon working as advertised. Anybody have working code yet? I'd like to figure that out, and compare it to my other concepts I'm working with. I'm not going to give it away yet, but I've come up with something that's very, very exciting... and very probably... just as good-looking.

3. I have found that if one doesn't use powers of 64 on the bitmaps, then artefacts tend to appear when the texture squares / rectangles are being rendered :P This is probably due to whatever compression is happening when the "sheet" is being "stitched", so it's probably going to be very difficult to fix. I just thought everybody should know it happens.

4. I've tried ExplosionSpeed out... and it needs to be made clear in the documentation that this is a multiplier. Making it gigantic isn't necessary- even a value of as little as 10 makes most explosions effectively instant. What I'd like, and what I'm having trouble achieving... is to have an explosion that is very non-instant- I was kind've hoping to fix the Demon's gun so that it's no longer a crude hack-around, but is a true area-denial weapon. However, this isn't working very well at this time.

5. I already noted the whole ImpulseFactor bug in a previous thread, but I'm noting it again here- it's definately a bug.

When the following are all zero, a weapon should never impart ImpulseFactor, cause craters, etc.:

Code: Select all

	impulsefactor=0;
	impulseBoost=0; 
	craterMult=0; 
	craterBoost=0;
This works for all model-based and gravity-effected projectiles, but not for BeamLasers. Should be a snap to fix, I'd think... just make the code that handles that match the others.




Some good things I should note here:

1. I've tested the new LOS code, and it's very useful, and works as advertised. I see very little degradation of LOS results with 2X, even with land units. Should be very useful for people wanting to test out long LOS mods.

2. The FX I have gotten working properly thus far are... just gorgeous. Period. I cannot wait to get them all working right!

3. I've noticed that water now reflects all weapons properly.[/code]
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Post by KDR_11k »

Beam weapons are checked for collision only a fixed number of times per second (probably 30), try firing a very fast (5000 units or more) beamweapon at the ground. Make sure the weapon causes craters, preferrably deep ones so you can see the point of impact wander once the crater is deep enough.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

So... er... they're being checked every tick? But so are the other projectile types, surely! What's going on here isn't that simple.

I've caught beams halfway through things, with a weaponvelocity of 600, for example! And their miss rate, no matter what their speed, is much higher with small objects than shots using projectiles! When I switched out all of the weapons using graphical effects and started using models... the difference in hit-rates was immediate... and completely compelling, from a game-design POV...
User avatar
AF
AI Developer
Posts: 20687
Joined: 14 Sep 2004, 11:32

Post by AF »

Would you find it useful if I provided a binary for you to test with that tiny size ramped up to a larger value? Just give me the new value and where to put it and I'll compile it.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Yes, that'd be very useful. However, I'm afraid that I'll just be shooting in the dark. I'm still reading through the relevant source, trying to figure out the major difference between model-based collision detections and lasers.

And... oh, boy... did I find yet more nuggets of useful stuff!

1. Didja know that there's a Boolean state "collideFeature"? And that it works like "collideFriendly"? So... you can have, for example, flamethrowers ignore those pesky DTs, but cannons won't!

2. And didja know that FLAMES CAN NOW USE TEXTURE1?!?!?! OMG! That means... er... well, without RGBcolor and RGBcolor2, I guess it doesn't mean much... yet... and there's no way to override it through the texture... yet... ah well. At least I'm no longer stuck with the default Flare ;)

3. And Shields can now have custom textures, too, from the looks of things!
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

If you're just getting here, the following post and my theorizing is definately completely wrong- see screenshot below.





Hmm. I'm still confused. Everything looks the same... except for tiny differences in lines (I dunno enough about C++ syntax to know if they really matter or not):

In Missile and ExplosiveProjectile, the following line, referring to the collision check, is like so:

Code: Select all

void CMissileProjectile::Collision(CUnit *unit)
In LaserCannon, one of the problem children, its like this:

Code: Select all

void CLaserProjectile::Collision(CUnit* unit)
So the position of the asterix (*) is different. I dunno whether that means anything. Other than that... and some serious differences in the way that the predictive aiming code is working, everything looks very simple. I wonder if the predictive code is the main problem? Lemme try making the RookGun a plasma cannon for a moment, and test that...

<comes back from tests>

YES!! IT'S THE PREDICTIVE AIMING CODE! I am 99% certain now!

Soooo.... basically, what's really going on here is that visually, the shots seem to be passing through things... the truth, however, is that they're never actually hitting the targets- the graphical effect just makes it appear that way! We need to use the (more complex, and probably with some sensible modifications) predictive code for ballistic weapons, instead of the code we're using now. I am now pretty darn certain. I've tested with non-model weapons... it's not that at all. It's that the darn things are just plain missing most of the time, because the vector being returned is off by a wide margin! That's why we see this with small things that are moving quickly, but not with large, slow things, or small, slow things!

Ahhhh... and the big fudge factor... the thing that's almost CERTAINLY borking this up... is that silly "experience" code. I'll betcha that when checked, it will be determined that the "experience error" introduced for these weapons is mostly to blame.

Well... er... I hate to put it this way, but should these weapons even be effected by Experience? Do we really want these weapons to be entirely useless as anti-aircraft weapons because of this, when the effect on practical ground combat is almost zero?

I guess there's a good way to find out... lemme find that code....




In LaserCannon.cpp, in the function void CLaserCannon::Fire(void):

Change the line:

Code: Select all

dir+=(gs->randVector()*sprayangle+salvoError)*(1-owner->limExperience*0.7)
To this:

Code: Select all

dir+=(gs->randVector()*sprayangle+salvoError)
Then compile a new version, and let's run this test again!
Last edited by Argh on 02 Sep 2006, 13:54, edited 1 time in total.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Hmmm.

Just to test a theory, given that math, I set the SprayAngle to 0. Given the math, that should force the shots to be without any errors, no matter what the Experience is.

Well... it worked. However... I'm back to Square One... and here's definitive proof:

Image

As you can clearly see... this projectile went straight through the center of the target S3O, but did not register a hit. Something is seriously awry :P
User avatar
Das Bruce
Posts: 3544
Joined: 23 Nov 2005, 06:16

Post by Das Bruce »

The internal spring clock runs at 30 frames a second right? So if the projectile travels faster than the diameter of the s3o divided by 30 per second or faster than it could be caused by spring not checking the intermediate points it travelled through.
hawkki
Posts: 222
Joined: 01 Jan 2006, 19:47

Post by hawkki »

I cannot imagine where the community would be it it was not for Argh. He is contributing so much time to this project that most og you dont even realize it.

He has brought so many features to the community knowledge and bugs already solved to the devs that it is just amazing. And the way he writes, you could compare it to some of the historys big talkers, he knows how to make you listen!
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

@Das Bruce:

If that's the case, then why, when I change this weapon to use a S3O model, does it work perfectly?

That is the question. Because I have zero doubt that that's exactly what happens. When I swapped out all BeamLasers/BeamWeapons in NanoBlobs 0.51b, I wasn't doing it because I wanted to- I was doing it because when I made the Wolf start to fly and shrank all the units to Epic scales, I immediately saw that the SpireRook's weapon was utterly failing to detect hits on Wolves and Knights. Switching to a solid model completely reversed this problem- it was the only thing I had to change. If I change it back to a model right now, I'll be back to where I was- with a properly-functional weapon again. I haven't altered the weaponvelocity throughout these experiments, and if you play NanoBlobs 0.51b or higher, you will see that SpireRooks shoot down Wolves just fine... these ones with the new FX, though, hit about once in 6 shots, which is useless.

We're back to that collision code and the minimum size stuff, then... ok, AF, lemme read through that again... and see what we need to change...
User avatar
Das Bruce
Posts: 3544
Joined: 23 Nov 2005, 06:16

Post by Das Bruce »

I would chance a guess that s3o models for weapons have some sort of hitsphere, increasing the speed at which it has to travel for it to quantum leap through its target. Although thats merely speculation.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Let's try this, AF, if you're still up for it. This is about the only thing I can think of... it might account for these problems:

We have lines like this in QuadField.cpp:

Code: Select all

		if(dir.x==0)
			dir.x=0.00001;

Code: Select all

		if(dir.y==0)
			dir.y=0.00001;
If I'm reading this right, which I'm probably not... this is what's determining the size of a "minimum-sized quad" for calculation of collisions. I thought that the minimum real size of any object in Spring was 1 Spring Cubical Unit, also known by Smoth's term, "Elmo". This doesn't appear to be true! I'm not sure what exactly we need to do with the minimum sizes here (i.e., I don't know what a value of 1.0000 is- whether that's an Elmo, or some gigantic scalar value)... but I think that's where our problem is. Pretty simply, these beamweapons and beamlasers are using something that is sooooo tiny, that I think it's getting squashed to a no-hit result sometimes by the code, when it shouldn't be.

However... somebody who can read this spaghetti with more competance should probably take a look. I'm just not sure if that's the value that gives us a final "quad-Elmo", or something else entirely... so upping that number to 0.1 may or may not result in desired hitscan behaviors...
User avatar
AF
AI Developer
Posts: 20687
Joined: 14 Sep 2004, 11:32

Post by AF »

Ok, I'll mail you it as I have to go somewhere very soon.

I've also suffixed the values with 'f' aka 0.1 vs 0.1f as they're float values not doubles.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Cool, I'll test it after I've gotten some sleep then. Maybe somebody will have taken a longer stroll through the sourcecode by then- I feel like I have achieved about 10% understanding of what I'm looking at :P
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Post by KDR_11k »

Notice that the experience error factor is always <=1.0 which means it will not increase the error.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

I tested AF's quickie compile (which also revealed that I'm going to need to redo my ColorAlphas, because I used DDS DXT1s without explicit alpha channels, not DDS DXT3s, which means that with Yeha's code, I am now seeing my units largely transparent, lol).

I think that that might've worked somewhat- it didn't cause seriously borked behaviors, let's put it that way. Hitrates seem to be up, but it's still not perfect, and I'm too tired to swear one way or another that it's right, and then there's the predictive value returned, too, which I am still leaning towards saying is wrong.

I think we need a value of over 1.0 to generate good hits every time- iow, a minimum of one Elmo. Maybe higher. It will occasionally cause minor visual discontinuities (i.e., shots hitting when it appears that they should be missing) but I'd strongly prefer that to the opposite case, where shots aren't registering hits when they very clearly are passing through the hitsphere radius...

And as for the ExperienceFactor... I don't think it works that way, but again, I'm too tired to argue the point. I noticed a distinct difference in the weapon vectors after forcing the errors to zero, but I'm tired and maybe I'm just seeing what I want to see there. I've never liked the Experience system in Spring, so I was just excited that I'd found a way to cut all of that out've NanoBlobs, really... too bad the unit's MaxDamage isn't so amenable to control, but that's a relatively minor wrinkle, really...
Last edited by Argh on 02 Sep 2006, 15:26, edited 1 time in total.
Tobi
Spring Developer
Posts: 4598
Joined: 01 Jun 2005, 11:36

Post by Tobi »

Code: Select all

vector<int> CQuadField::GetQuadsOnRay(float3 start, float3 dir, float length) 
The map is divided in "quads" to speed up collision detection, ie. from average case complexity O(n^2) to worst case O(n^2), average case O((n/m)^2), with n=number of units, m=number of quads.

This function builds a list (vector<int>) of quads through which the ray from start in the direction dir with length length passes.

These list is (I assume) then used later on in the following function you copied:

Code: Select all

// optimization specifically for projectile collisions 
 void CQuadField::GetUnitsAndFeaturesExact(const float3& pos, float radius, const vector<int>& quads, vector<CUnit*>& dstunits, vector<CFeature*>& dstfeatures) 
This one seems to put all units in a list of quads, in a list of units, and all features within a certain radius from a certain pos in a list of features.

I don't understand why it just takes all units and does a distance calculation for features though...
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

I think that GetQuadsOnRay is being used for BeamLaser collision detection, which, along with BeamWeapons, is not behaving correctly. Which is why I dragged that one up.

BeamLasers are a bunch of massive mysteries to me. For example, it's quite clear and easily seen that they're really acting like a BeamWeapon that's firing very rapidly, to preserve the illusion of a single, large beam- the graphical effect and the actual effect of the weapon are two entirely different things. BeamLasers are, very clearly, firing multiple shots- if you've ever watched one with a super-high Tolerance, you'll see the explosion effects trailing far behind the drawn position of the beam, which always appears to be perfectly at the first location the target is at.

I dunno why modders aren't given control of that behavior, which is just a specific variation on Burst (BeamTime effectively is a burst that also divides up the damage done, which was annoying until I figured out how it works).
Tobi
Spring Developer
Posts: 4598
Joined: 01 Jun 2005, 11:36

Post by Tobi »

Argh wrote: We have lines like this in QuadField.cpp:

Code: Select all

		if(dir.x==0)
			dir.x=0.00001;

Code: Select all

		if(dir.y==0)
			dir.y=0.00001;
If I'm reading this right, which I'm probably not... this is what's determining the size of a "minimum-sized quad" for calculation of collisions.
You're taking the code out of context. Notice the if()'s around it:

Code: Select all

   if(start.x<1){ 
       if(dir.x==0) 
          dir.x=0.00001; 
       start=start+dir*((1-start.x)/dir.x); 
    } 
    if(start.x>gs->mapx*SQUARE_SIZE-1){ 
       if(dir.x==0) 
          dir.x=0.00001; 
       start=start+dir*((gs->mapx*SQUARE_SIZE-1-start.x)/dir.x); 
    } 
    if(start.z<1){ 
       if(dir.z==0) 
          dir.z=0.00001; 
       start=start+dir*((1-start.z)/dir.z); 
    } 
    if(start.z>gs->mapy*SQUARE_SIZE-1){ 
       if(dir.z==0) 
          dir.z=0.00001; 
       start=start+dir*((gs->mapy*SQUARE_SIZE-1-start.z)/dir.z); 
    } 
 
So the code you changed (if I followed you correctly), is only executed if the start of the ray is on the very edge of the map, or outside the map.

It should never be possible that changing these numbers influences mid-map collision detection.
User avatar
Nemo
Spring 1944 Developer
Posts: 1376
Joined: 30 Jan 2005, 19:44

Post by Nemo »

Laser hit detection has been a problem for a good while - if you've played Star Wars Spring 1.0, you'll find after a competitive game or two that battledroids own EVERYTHING. This is NOT because their stats are overpowered - its the fact that they are so small, and blaster weapons move so quickly, that barely a third of the shots that hit them actually register as hits. Once their model size was increased, they were balanced quite well.

I was having a brutal time of this with 1944 as well, until I just slowed down all the weapons to a velocity of ~400 (formerly 1000). It makes the bullet illusion a little weak, but its not too bad, since they're still faster by a factor of 20 than anything else.

The short version: Weapon (most notably lasers) hit detection is an issue with small units and fastish weapons. It would be fantastic if it got some attention from a dev.
Post Reply

Return to “Engine”