Game Development Community

Minimum linear velocity misbehavior?

by Tim Doty · in Torque Game Builder · 04/22/2005 (3:57 pm) · 8 replies

It's the darnedest thing. The game I'm working on has cars which can launch missiles at each other (a game for Road Rage). The missiles have been around virtually unchanged for some time and work great. Until I just noticed something: if you are turned to a narrow band (directly away from the enemy) and launch the missile it just sits there.

Well, not quite true. The targeting function is called and it rotates to face the target -- but it never budges. You twitch a little outside of this narrow band and the missile roars off. I haven't tested thoroughly enough to know the exact edges, but -175 degrees is one of the failure points.

Test and test. What is going on is somehow the missile is being reduced below its minimum linear velocity and set to rest. Why it only happens in those degree ranges I don't know. Literally, the velocity is set (and is equal to the minimum velocity), the targeting function is scheduled for 100ms later and the launch function ends. No other code. Everything is event driven at this point. The missile's aren't named or stored in a variable so no other place in the script even knows about them. But between having its velocity set and the target function being called for the first time the linear velocity drops below the minimum velocity and thus to zero.

Why do I know it's the min velocity setting? Because removing it (it isn't really necessary anyway) solves the problem. Add it back in and the problem comes back. Whatever is messing up the velocity is being caused by the angle to the target: this is independent of absolute rotation, but anything close to -180 from target causes the problem.

Mostly posting out of frustration. I look at the code and I just can't see anything that is setting or colliding with the missile. This is where being able to step through code in a debugger sheds light.

My fix is to remove the missile velocity as it really isn't needed anyway. Just annoying that I can't figure out what I'm doing wrong.

#1
04/22/2005 (11:37 pm)
Tim, can you post your code so we can check out what's up? Thanks. :)
#2
04/23/2005 (5:23 am)
The reason I hadn't posted code is because I don't know where the velocity is getting knocked below minimum. There's a lot of code and I truly don't know what is relevant. Since you asked, here's some that is relevant to the missile being created and it targeting. There really shouldn't be any other code affecting it directly. This isn't nice and tidy code. I recently did some code cleanup, but it isn't pretty and I haven't yet written all intended accessors.

////////////////////////////////////////
// Missile Object Class
link(missileObject, "", fxStaticSprite2D);

function missileObject::create(%objectName, %missileType, %target, %coord, %angle, %velocity) {
  %this = new fxStaticSprite2D(%objectName) {
    superClassName = "";
    className = missileObject;
    scenegraph = %target.scenegraph;
    dataBlock  = %missileType.getImageMap();
    };
  missileObject::protInit(%this, %missileType, %target, %coord, %angle, %velocity);
  return %this;
  }

function missileObject::protInit(%this, %missileType, %target, %coord, %angle, %velocity) {
  %this._objectModel = %missileType;
  %this._target = %target;
  %this.setSize(%missileType.getSize());
  %this.setImageMap(%this._objectModel._imagemap);
  
  // Set missile collision info
  %this.setGroup(2);
  %this.setLayer(2);
  %this.setCollisionSuppress(false);
  %this.setCollisionActive(true, true);
  %this.setCollisionMaterial(missileMaterial);
  %this.setCollisionPhysics(true, true);
  %this.setCollisionMasks( BIT(1) | BIT(2), BIT(1) | BIT(2));
  %this.setCollisionPolyCustom(%this._objectModel._polySides,%this._objectModel._polyVertices);
  %this.setCollisionCallBack(true);
  %this.setLifetime(5); // 2 & 3 sec was just too short, possibly 5 would be better?

  // setup exhaust animations
  %this.exhaust = new fxParticleEffect2D() { scenegraph = %target.scenegraph; };
  %this.exhaust.mount(%this, "0 1");
  %this.exhaust.loadEffect("AutoArena/client/effects/MissileExhaust.eff");
  %this.exhaust.playEffect();
  
  // Set missile data
  %this.setMaxLinearVelocity(%this._objectModel._maxVelocity);
  %this.setMinLinearVelocity(%this._objectModel._minVelocity);
  %this.setMaxAngularVelocity(%this._objectModel._maxTurn);
  %this.setMinAngularVelocity(%this._objectModel._minTurn);
  %this.setPosition(%coord);
  %this.setRotation(%angle);
  %this.setLinearVelocityPolar(%angle,%velocity + %this._objectModel._minVelocity);// + %this._objectModel.getMaxAcceleration());

  %this.schedule(100,"Target");
  }


function missileObject::Target(%this) {
  // find necessary turn
  %angle = getWord(vectorToPolar(vectorSub2D(%this._target.getPosition(),%this.getPosition())),0);
  // adjust polar linear velocity for the turn
  %energy = vectorLength2D(vectorSub2D(%this.getLinearVelocity(),vectorFromPolar(%this.getRotation(),vectorLength2D(%this.getLinearVelocity()))));
  if (%energy < %this._objectModel.Stability) {
    %this.setLinearVelocityPolar(%this.getRotation(),vectorLength2D(%this.getLinearVelocity()));
    }
  else {
     %this.setLinearVelocity(vectorAdd2D(vectorFromPolar(%this.getRotation(), %this._objectModel._stability), vectorScale2D(vectorNormalise2D(%this.getLinearVelocity()), vectorLength2D(%this.getLinearVelocity()) - %this._objectModel._stability)));
    }
  %delta = ClampAngle(%angle - %this.getRotation());
  if (%delta > %this.getMaxAngularVelocity()) %delta = %this.getMaxAngularVelocity();
  else if (%delta < %this.getMinAngularVelocity()) %delta = %this.getMinAngularVelocity();
  %this.setRotation(%this.getRotation() + %delta);
  %this.setImpulseForcePolar(%this.getRotation(),%this._objectModel.getMaxAcceleration());
  %this.schedule(100,"Target");
  }
#3
04/23/2005 (5:23 am)
Under protInit() if I comment out the line %this.setMinLinearVelocity() the problem goes away. If I change the line in protInit() to increase the setLinearVelocityPolar() by one the problem has (under the testing I've done) gone away. Note that the vehicle was stationary (i.e., not moving backwards) during testing -- although that is reason enough to not set the minimum linear velocity.

The complicated movement code for the missile achieves slippage. Because it can only turn so quickly it will end up sliding sideways through the air when it is going very fast and with a velocity vector mostly perpendicular to the directed line pointing toward the target. This is intended. The setRotation() was because I was having trouble ensuring that the missile was turning (and not turning) appropriately using setAngularVelocity(). With this code I can ensure that the missile does not turn more than the desired number of degrees and is pointing as closely as allowed toward the target.

The ClampAngle() function ensures that the angle is locked between -180 and 180 degrees. vectorFromPolar() and vectorToPolar() functions are helper functions I wrote to convert between polar and rectangular vectors.
#4
04/23/2005 (5:48 am)
@Tim - What are you trying to do with setMinLinearVelocity()?

I know I personally thought that setting a minimum velocity would cause an objects velocity to not drop below the value I've set. But it is actually used to set a point where the object will completely stop when its velocity drops below this point. (I'm not sure that was the best way for me to word that :P). For example, if you have a ball bouncing, the physics system might cause the ball to continuously bounce because its velocity never reaches zero. By increasing the minimum velocity to 1 (or something), the ball will come to a complete stop.

This may not have anything to do with what you're asking, so sorry if I'm just running off at the mouth :)
#5
04/23/2005 (7:38 am)
@Tim: Chris is correct and the naming of the function is probably the reason for the confusion. I do have it on my list to change the name of this function (it was discussed as well as other related work but it cannot happen until the new file-format is in, that's why it didn't get put in the last update.

Essentially, it acts to set the object at rest when it hits this minimum velocity whereas the maximum velocity simply clamps it. Having a minimum velocity can actually create some bizarre effects because if the object will not stop, it can easily interpenetrate objects and go flying off into infinity. There's also the issue of ambiguity. If an object with an existing velocity has another force applied to it, at what point is the "minimum velocity" applied? Clamp the new force so that it doesn't reduce the velocity below the minimum or clamp the speed of the object after the operation? Depending on which operation you use, you can either end up with only components of the new force being used or end up with a form of gimbal lock if you find yourself in a situation where you want to apply a force but it will be ignored because it causes the resultant speed to be below the minimum.

In the end, its use is limited and something like a guided or directional class would need to be defined. You may be happy to know that I've been playing with the idea of adding an option that provides for a sense of direction to at least the rendering of T2D objects based upon their velocity. This would work in a similar fashion to the "keep-aligned" mode in the particle-engine where the object is orientated according to its velocity.

The existing naming of the function is very misleading though and needs to change to something like "rest velocity".

Sorry for the confision.

- Melv.
#6
04/23/2005 (2:55 pm)
@Chris: the missile stuff has been around a while and if something isn't in front of me I tend to forget details, but I think originally I had some concept of it not allowing the missile velocity to drop below its minimal amount. It is really superfluous in my code and I realize that. And I found your explanation to be quite clear. Thanks.

@Melv: thanks for taking the time to respond and explain. If I understand you correctly the orientation by velocity would be an interesting option and I think quite handy for some things. The missiles having (potentially) a different facing to their velocity is deliberate, but I can think of cases where having it always point forwards (relative to velocity) would be very nice.
#7
04/24/2005 (2:08 am)
Absolutely Tim. The ability to orientate a T2D object according to its direction could be very handy but it obviously overrides the rotational functionality, even rigid-collision response though.

Definately a good thing to have but I'll only put it in if it doesn't cause too much confusion elsewhere which hopefully it won't.

On my list Tim. :)

- Melv.
#8
04/24/2005 (5:35 am)
@Melv: as a switch that defaults to off I don't think it would cause undue confusion. Any comments about the camera rotation suggestion?