Game Development Community

Shooting the Tank?

by NUTS! · in Torque Game Builder · 12/11/2006 (8:16 pm) · 45 replies

I have been trying to figure out how to get the tank to shoot from the TDN tank physics example. This is what my shoot code looks like:
function shoot() {
    %tankPosition = turret.getposition();
   %mousePosition = sceneWindow2D.getMousePosition();
   %shotAngle = t2dAngleBetween(%tankPosition, %mousePosition);
    %bullet = new t2dStaticSprite(){ scenegraph = sceneWindow2D.getSceneGraph();};
   %bullet.setImageMap ( "ggLogoImageMap" );
    %bullet.setSize = "8 8";
   %bullet.setLinearVelocityPolar ( $shotAngle, 100); // shoot at speed 100
    $nextScheduledShot = schedule(500, 0, "shoot");
}

I can get the tank to show up and drive around. However, when I shoot I just get the bullet starting from the center and going to the top, the console error I get is this:

ImageMap(tankChasisImageMap) Error: 'Invalid Bitmap File' (2)
Tanks/gameScripts/PhysTank.cs (24): Register object failed for object tankChassisImageMap of class tdImageMapDatablock.

Which doesn't make any sense why I get that.

My PhysTanks.cs line 4 through 24 looks like :
new t2dImageMapDatablock(tankChassisImageMap) {
      canSaveDynamicFields = "1";
      imageName = "./images/tankChassis";
      imageMode = "FULL";
      frameCount = "-1";
      filterMode = "SMOOTH";
      filterPad = "0";
      preferPerf = "0";
      cellRowOrder = "1";
      cellOffsetX = "0";
      cellOffsetY = "0";
      cellStrideX = "0";
      cellStrideY = "0";
      cellCountX = "-1";
      cellCountY = "-1";
      cellWidth = "0";
      cellHeight = "0";
      preload = "1";
      allowUnload = "0";
      defaultConfig = "PhysTankDatablock";
   };

As you can tell by no means can I script. So any help would be greatly appreciated.
#21
12/17/2006 (10:09 am)
Try this with the function creating the turret:
// create and attach a turret to this tank
function PhysTank::initializeTurret(%this)
{
   // create the new turret and store it on this object
   %this.turret = new t2dStaticSprite()
   {
      sceneGraph = %this.scenegraph; // make sure to set the scenegraph
      ImageMap = "tankTurretImageMap"; // set the image to use
      config = "PhysTankTurretDatablock"; // set the config datablock

      LinkPoints = " 0 -0.7 "; // link point for projectile
   };

   // mount the turret to the tank and set 'trackRotation' (fourth argument)
   // to false so we can rotate our turret independently
   %this.turret.mount(%this, "0 -0.1", 0, false, true, true, true);
}

Next, and I confess I'm not entirely sure this will work, try making the shoot() function use %this for getting the link point:
function shoot(){
      //determine shot angle
      %tankPosition = %this.getPosition();
      %mousePosition = sceneWindow2D.getMousePosition();
      %vec = t2dVectorSub(%tankPosition, %mousePosition);
      %shotAngle = -mRadToDeg(mATan(getWord(%vec,0),getWord(%vec,1)));
      $nextScheduledShot = schedule(200, 0, "shoot");
      
   //create the projectile
     %bullet = new t2dStaticSprite(){ scenegraph = sceneWindow2D.getSceneGraph();};
     %bullet.setPosition( %tankPosition );
     %bullet.setImageMap ( "gglogoImageMap" );
     %bullet.setSize = "4 4";
     %bullet.setLinearVelocityPolar ( %shotAngle, 100);  // shoot at speed 100
   // shedule the next sho in 500 mili
   
}

I'll keep reading through your scripit, but in the meantime thy that and let me know how it goes.

- Don

Edit: I don't know that you necessarily need a separate datablock for the projectile, but it would be useful later on if you wanted to have different shot types. You'd make a datablock for each and then choose based on ammo, powerup, or whatever. So, it might make things easier to modify later on.
#22
12/17/2006 (12:10 pm)
Check the domain and range of the mAtan function. You may be getting results that are constrained to a 180 arc (centered on zero) which will cause quite erratic behavior. You can check by feeding your code different values for x and y and seeing the results:

-mRadToDeg(mATan(%x, %y));

Feed in some known values like (0, 1), (1, 0), (-1, 0), (0, -1), (2, 2), etc. and make sure the angle you get out is what you expect. Otherwise you're just doing your math wrong.
#23
12/17/2006 (1:04 pm)
@don
When I tried the %this I got the similiar that it was unable to fine. however, if I add

%this = "turret";

To the top, then I do not get an error. Sadly the bullets still look to be coming from the center of the tank still. Even If I adjust those numbers.
#24
12/17/2006 (1:12 pm)
Ben, I changed my fire code to this:
%shotAngle = mRadToDeg(mAtan(%dx, -%dy));
because before the shots were indeed firing at random it seemed as you mentione. So NUTS! that might be the problem you are having too (If you are still having that problem) you might want trying changing:
%shotAngle = -mRadToDeg(mATan(getWord(%vec,0),getWord(%vec,1)));
to the code Ben and I mentioned. You may have to play with where x and y live and if they need to be negative or not.


Don, I tried the changes into my code, but %.this.getposition(); resulted in the following error:
resources/Tank/PhysTank.cs (276): Unable to find object: '' attempting to call function 'getPosition'
t2dVectorSub() - Invalid number of parameters!
t2dSceneObject::setPosition() - Invalid number of parameters!
resources/Tank/PhysTank.cs (276): Unable to find object: '' attempting to call function 'getPosition'

And the result was if I moved the mouse the shot would orignate from where the mouse as at the time I hit fire, unless I was not moving the mouse then it would fire from the middle of the screen which sounds like what NUTS! is running into. To note though the turet was still tracking the mouse movements.

So I started wondering, what if this line of code was changed:
%bullet.setPosition( %tankPosition );
so instead of using %tankPosition that it could call the link ID? Which the link code would have to be changed to setLinkPoint(1, 0, -0.7); and instead of you would have %bullet.setPosition( getLinkPoint(1) );
Does that sound like something that might work?
#25
12/17/2006 (2:56 pm)
To clarify, the tank has no problems shooting :). All I did is steal code from the function PhysTank::updateTurret. All that seems to have done is take 3 lines of code and combine it into one.
#26
12/17/2006 (3:01 pm)
Hrmm. The solution is really close at hand, I think I'm still not understanding exactly how the tutorial is working. The way my project is setup, it's the sprite object that is calling the function - when I press spacebar, the left mouse button or the A button on my controller it's telling the sprite "You need to start the fire function." as opposed to directly calling the fire function.

So, when this is pressed
controllerMap.bindCmd( keyboard,  "space", "$player.primaryFire();", "$player.primaryFireStop();" );

It first goes here:
function Controller::primaryFire( %this )
{
    if( ! isObject( %this.attachedObject ) )
    {
        return;
    }
    
    %this.attachedObject.beginPrimaryFire();
}
Now in this case, I called the function with $player which goes to the function as %this - the calling entity. All the primaryFire( %this ) function is doing is finding out who's firing - i.e. what is the attached entity that's firing?

From there, it launches the beginPrimaryFire(); function passing along the attached entity.
function Player::beginPrimaryFire( %this )
{
    %this.firingPrimary = true;
    %this.fire();
}

Your eyes aren't decieving you, this is simply toggling a flag and launching yet another function.

This is the meat of it for the player firing. If you read through it, you can see where I'm actually switching between linkpoints as the player holds down the button, giving an alternate firing effect. This is where I'm using the ability to get the link point of the attached object; specifically in the line %projectile1.setPosition( %this.getLinkPoint( %linkPoint ) );
function Player::fire(%this)
{
    if( %this.scenegraph.getScenePause() )
    {
        %this.schedule( 2000, fire );
        return;
    }
    
    //  Check to see if we are no longer firing
    if( ! %this.firingPrimary )
    {
        return;
    }
    
    if( %this.currentFire == 0 )
    {
        %this.currentFire = 1;
        %linkPoint = %this.fireLinkPoint1;
    }
    else
    {
        %this.currentFire = 0;
        %linkPoint = %this.fireLinkPoint2;
    }
        
    %projectile1 = new t2dStaticSprite() 
            { 
                scenegraph = Game_01SceneGraph; 
                class       = SmallParticleCannon;
            };
    %projectile1.setPosition( %this.getLinkPoint( %linkPoint ) );
    %projectile1.setSize( "1.5 3" ); 
    %projectile1.setImageMap( y_laserImageMap );
    %polarAngle1 = %this.getRotation() + getRandom(-%this.fireAngle/2,%this.fireAngle/2);
    %projectile1.setLinearVelocityPolar(  %polarAngle1, t2dVectorLength(%this.getLinearVelocity()) + 100 );
    %projectile1.setRotation( %polarAngle1 );
    %projectile1.setGraphGroup( %this.getGraphGroup()+1 );
    %projectile1.setLayer( %this.getLayer()+1 );
    %projectile1.setCollisionActive(false, true);
    %projectile1.setCollisionPhysics(false, false);
    %projectile1.setCollisionMaterial(projectileMaterial);
    %projectile1.setCollisionMasks( $ordinanceCollisionGroups );
    %projectile1.setCollisionPolyPrimitive( 3 );
    %projectile1.setCollisionCallback( true );
    %projectile1.setLifetime( 1.5 ); // bullet lasts for X seconds before being deleted
    %projectile1.tag = "SmallParticleCannon";
    %projectile1.team = %this.team;
    %projectile1.owner = %this;
    %projectile1.damage = 1;
    
    // Player Fire Audio.
    alxPlay( playerFireAudio );    

    // Reschedule another fire.
    %this.schedule( %this.fireRate, fire );
}

Is that any clearer of have I hopelessly derailed your thread?
#27
12/17/2006 (3:19 pm)
@Don
Its very helpfull. I see you used
%projectile1.setPosition( %this.getLinkPoint( %linkPoint ) );

When I do
%bullet.setPosition( %this.getLinkPoint( %linkPoint ) );

Its starts shooting in the center again.(o script errors in console, again, need to look up echo how to) The shots still follow the mouse and all, just not attached to the tank. I think somewere along the lines I don't have %this named correctly?
#28
12/17/2006 (3:51 pm)
What I'm seeing is that your function shoot() is being called with no parameters to tell it what entity is shooting.

%this has no meaning in shoot() because that function is launched as a schedule call from the onMouseDown() event.

$nextScheduledShot = schedule(100, 0, "shoot"); only assigns a global variable to the schedule, it doesn't contain any information about what's doing the shooting, so you can't get a linkpoint and thus your errors.

My last example was showing that from the very beginning key press I keep passing along the entity to every function so that each one could use %this if needed.
#29
12/17/2006 (6:43 pm)
Crap, looks like I have some re writing (re scripting?) to do.
#30
12/17/2006 (8:12 pm)
NUTS! I was thinking along that same line too. Perhaps re-write the current mini tutorial to bring it more inline with the starter tutorials incuding using dynamic fields on the sprite object for the major variables instead of having them coded in through the .cs file and also include the firing code the submit it to TDN.
What do you think of turning this thread into such a project?
#31
12/17/2006 (9:27 pm)
I think it would be fun. However, I lack time and talent :). I just had some time this weekend to work on it. I will continue to hack at it though. It will probably take me a long time, I am very new to all this. I do not have a computer degree or even work in the field. This will probably be my first seriouse attempt at making something work were I don't copy and paste (freakin local and global, bah). First thing I need to do is go back and review the tutorials.
#32
12/18/2006 (4:38 am)
I'd say you guys are making great progress and this is exactly how I got to the point I'm at with my game. I jumped in because it was exactly the sort of problem I'd worked through when I started with TGB. I also have the advantage of a programmer friend who's working with me, and I've learned a lot from him.

We could certainly make what you have work properly, and save you from re-writing if you like.
#33
12/18/2006 (6:05 am)
@Don
That would be awesome!
#34
12/18/2006 (8:27 am)
Umm why can't I add another link point to the turret in TGB? (which I did) How can I get the bullet to see that? Google Time!
#35
12/18/2006 (7:21 pm)
Heya NUTS! Ok, to get this project back on track for you, let's have a look at your current script. Is it all in one file? Is it small enough to post here?
#36
12/18/2006 (7:42 pm)
Currently it is all on one .cs, which I am reading is not a good thing. Basically all I did was copy the tank physics stuff from TDN. However, I moved them out of the resource folder and created its own folder, called Tank. I just copied the datablock information from the resource folder and added it to the PhysTank.cs. So the vast majority of it is the same, most the changes are in the top half. The current code that I am going to copy/paste has the shooting coming from the center because it can not find the link point. I am still doing a little research on that. I noticed that in TGB that you can edit link points so I added another another one to the turret. Now I am just trying to find out what its called. Again, I bet echo would be helpfull.
Ok here we go:
// Tank Datablock definition
// play with these variables to change the handling of your Tank!

new t2dImageMapDatablock(tankChassisImageMap) {
      canSaveDynamicFields = "1";
      imageName = "~/Tanks/data/images/tankChassis";
      imageMode = "FULL";
      frameCount = "-1";
      filterMode = "SMOOTH";
      filterPad = "0";
      preferPerf = "0";
      cellRowOrder = "1";
      cellOffsetX = "0";
      cellOffsetY = "0";
      cellStrideX = "0";
      cellStrideY = "0";
      cellCountX = "-1";
      cellCountY = "-1";
      cellWidth = "0";
      cellHeight = "0";
      preload = "1";
      allowUnload = "0";
      defaultConfig = "PhysTankDatablock";
   };

new t2dImageMapDatablock(tankTurretImageMap) {
      canSaveDynamicFields = "1";
      imageName = "~/Tanks/data/images/tankTurret";
      imageMode = "FULL";
      frameCount = "-1";
      filterMode = "SMOOTH";
      filterPad = "1";
      preferPerf = "0";
      cellRowOrder = "1";
      cellOffsetX = "0";
      cellOffsetY = "0";
      cellStrideX = "0";
      cellStrideY = "0";
      cellCountX = "-1";
      cellCountY = "-1";
      cellWidth = "0";
      cellHeight = "0";
      preload = "1";
      allowUnload = "0";
      defaultConfig = "PhysTankTurretDatablock";
   };
   
datablock t2dSceneObjectDatablock(PhysTankDatablock)
{
   class = "PhysTank"; // associate this datablock with the PhysTank namespace
   maxSteerAngle = "55"; // the maximum angular force used for steering the tank (not an angle)
   cTrac = "60"; // coefficient of traction (max speed)
   size = "8.120 8.120"; // default size
};

// Turret Datablock definition
datablock t2dSceneObjectDatablock(PhysTankTurretDatablock)
{
   name = turret;
   class = "PhysTankTurret";
   turnSpeed = "100"; // speed at which turret turns
   size = "2.755 11.132"; // default size
};

// PhysTank onLevelLoaded callback
function PhysTank::onLevelLoaded(%this, %scenegraph)
{   
   // set this tank's name so SceneWindow2D can find it
   %this.setName("MyTank");
   
   // init tank toggle vars
   %this.gasPedal = false;
   %this.brake = false;
   %this.left = false;
   %this.right = false;
   %this.turretLeft = false;
   %this.turretRight = false;
   
   // init tank input vars
   %this.throttle = 0; // ranges from -1 to 1 (full reverse to full forward)
   %this.steering = 0; // ranges from -1 to 1 (full left to full right)
   %this.lastMousePos = "0 0";
      
   // init tank physics vars
   %this.latSpeed = 0;
   
   // set up turret
   %this.initializeTurret();
   
   
   // map keys
   moveMap.bindCmd(keyboard, "w", %this @ ".throttleOn();", %this @ ".throttleOff();");
   moveMap.bindCmd(keyboard, "s", %this @ ".brakeOn();", %this @ ".brakeOff();");
   moveMap.bindCmd(keyboard, "a", %this @ ".leftOn();", %this @ ".leftOff();");
   moveMap.bindCmd(keyboard, "d", %this @ ".rightOn();", %this @ ".rightOff();");
   
   // enable the timer callback for this object for every 25 milliseconds
   %this.setTimerOn(25);
}

// create and attach a turret to this tank
function PhysTank::initializeTurret(%this)
{
   // create the new turret and store it on this object
   %this.turret = new t2dStaticSprite()
   {
      sceneGraph = %this.scenegraph; // make sure to set the scenegraph
      ImageMap = "tankTurretImageMap"; // set the image to use
      config = "PhysTankTurretDatablock"; // set the config datablock
      //$turret.fireLinkPoint = $turret.addLinkPoint( "0 -0.20" );
   

   };
   // mount the turret to the tank and set 'trackRotation' (fourth argument)
   // to false so we can rotate our turret independently
   %this.turret.mount(%this, "0 -0.1", 0, false, true, true, true);
}

// this is called every time the mouse cursor is moved
function sceneWindow2D::onMouseMove(%this, %modifier, %worldPos, %clicks)
{
   // update MyTank's lastMousePos variable with fresh coordinates
   MyTank.lastMousePos = %worldPos;
}

// this is our update turret function to rotate the turret 
// towards the mouse pointer
function PhysTank::updateTurret(%this, %mousePos)
{
   // get a vector from the tank to the mouse pointer
   %vec = t2dVectorSub(%this.getPosition(), %mousePos);
   
   // get the desired angle required to point at the mouse pointer   
   %angle = -mRadToDeg(mATan(getWord(%vec,0),getWord(%vec,1)));
   
   // tell the tank turret to rotate towards that angle at a rate of 
   // 'turret.turnSpeed' which was set in our turret's datablock
   %this.turret.rotateTo(%angle, %this.turret.turnSpeed);
}

   function sceneWindow2D::onMouseDown(){
      // start shootig in 100 mili sec
      $nextScheduledShot = schedule(100, 0, "shoot");
   }
   function sceneWindow2D::onMouseUp() {
      //stop shooting
      cancel($nextScheduledShot);
   }

   function shoot(){
      //determine shot angle
      //%this.fireLinkPoint = %this.addLinkPoint( "0 -0.7" );
      
      %tankPosition = turret.getPosition();
      %mousePosition = sceneWindow2D.getMousePosition();
      %vec = t2dVectorSub(%tankPosition, %mousePosition);
      %shotAngle = -mRadToDeg(mATan(getWord(%vec,0),getWord(%vec,1)));
      $nextScheduledShot = schedule(200, 0, "shoot");
      
   //create the projectile
     %bullet = new t2dStaticSprite(){ scenegraph = sceneWindow2D.getSceneGraph();};
     //%linkPoint = LinkPoints;
     %bullet.setPosition( %tankPosition.getLinkPoint(%tankPosition.fireLinkPoint) );
     %bullet.setImageMap ( "smallBubbleImageMap" );
     %bullet.setSize = "4 4";
     %bullet.setLinearVelocityPolar ( %shotAngle, 100);  // shoot at speed 100
   // shedule the next sho in 500 mili
          
}


// PhysTank onTimer callback
function PhysTank::onTimer(%this)
{

rest is the same. Additionally I am calling the script from the main.cs, instead of game.cs. I find that when I do that it recognizes the datablocks.

Of course I would be happy with any help and feedback, Thanks!
#37
12/18/2006 (9:17 pm)
One thing tonight that just clicked and I am not sure why I had not though of this before or if it really matters that much, but part of the problem may be that the turret rotation and the fire code are on two seperate rotators totally independent of each other.
Would it be possible to just use the information already gathered by the turret instead of re-gathering the mouse position just for the fire code?
#38
12/18/2006 (9:37 pm)
Woohoo, I had a minor break through right after posting!
I did a little re-write of the original physTank file so I could rely more on dynamic fields and also so I could mount the turret my self in the editor so it would make the task of setting mount points easier, mind you it looks pretty much like the original, but there is actually some stuff cut out. With easier to set link points there is one problem down at least I think. I did this sort of based on the shooter tutorial where all of the tank code is in a player.cs file inside the /scripts folder


First I put the tank down, then I brough in the turret and and clicked on mount this object to another object and stuck it to the tank.
In the edit section for the turret under scripting I named the turret, well turret :-p
Next I went to mounting and made sure that track rotation was off, if not the turret points where the tank points.

On the tank body I added these dynamic fields:
cTrac = "15";
maxSteerAngle = "55";
turretTurnSpeed = "80";

Under scripting I named the tank p1Tank and for the class I called it player1Tank then I set to work choping up the code which is here below (no fire code):
function player1Tank::onLevelLoaded(%this, %scenegraph)
{
	 
	 $p1Tank = %this; //set the player's ship name to the instance
	
	 // init tank toggle vars
	 %this.gasPedal = false;
   %this.brake = false;
   %this.left = false;
   %this.right = false;
   %this.turretLeft = false;
   %this.turretRight = false;
   %this.throttle = 0; // ranges from -1 to 1 (full reverse to full forward)
   %this.steering = 0; // ranges from -1 to 1 (full left to full right)
   %this.lastMousePos = "0 0";

   //keybindings
   moveMap.bindCmd(keyboard, "w", %this @ ".throttleOn();", %this @ ".throttleOff();");
   moveMap.bindCmd(keyboard, "s", %this @ ".brakeOn();", %this @ ".brakeOff();");
   moveMap.bindCmd(keyboard, "a", %this @ ".leftOn();", %this @ ".leftOff();");
   moveMap.bindCmd(keyboard, "d", %this @ ".rightOn();", %this @ ".rightOff();");
   
      // enable the timer callback for this object for every 25 milliseconds
   %this.setTimerOn(25);   
}

//turret control
// this is called every time the mouse cursor is moved
function sceneWindow2D::onMouseMove(%this, %modifier, %worldPos, %clicks)
{
   // update MyTank's lastMousePos variable with fresh coordinates
   p1Tank.lastMousePos = %worldPos;
}
// this is our update turret function to rotate the turret 
// towards the mouse pointer
function player1Tank::updateTurret(%this, %mousePos)
{
   // get a vector from the tank to the mouse pointer
   %vec = t2dVectorSub(%this.getPosition(), %mousePos);
   
   // get the desired angle required to point at the mouse pointer   
   %angle = -mRadToDeg(mATan(getWord(%vec,0),getWord(%vec,1)));
   
   // tell the tank turret to rotate towards that angle at a rate of 
   // 'p1Tank.turretTurnSpeed;'
   $turretTurnSpeed = p1Tank.turretTurnSpeed;
   turret.rotateTo(%angle, $turretTurnSpeed);
}

// input functions 
// player1Tank onTimer callback
function player1Tank::onTimer(%this)
{
   // 1. interpret input flags
   // Throttle:
   // if only the gas pedal is pressed and we aren't reversing
   if((%this.gasPedal && !%this.brake) && %this.throttle >=0)
   {
      // increment trottle (forward)
      %this.throttle += 0.005;
      
      // clamp throttle at 1 for full forward
      if(%this.throttle > 1)
      {
         %this.throttle = 1;
      }
   }
   // if only the 'brake' is pressed and we aren't going forward
   else if((%this.brake && !%this.gasPedal) && %this.throttle <= 0)
   {
      // decrement throttle (reverse)
      %this.throttle -= 0.005;
      
      // clamp throttle at -1 for full reverse
      if(%this.throttle < -1)
      {
         %this.throttle = -1;
      }
   }
   // either *neither* or *both* gas petal and throttle are activated
   // -or- we are trying to throttle in the opposite direction we are moving
   else
   {
      // lower the throttle from either direction by 0.02
      // if magnitude of throttle is less than 0.02 then set to 0
      // Note: without checking for this, our throttle could skip between
      // negative and positive as it tries to hit zero. 
      // for example, immagine the throttle is 0.1
      if(%this.throttle < -0.02)
      {
         %this.throttle += 0.02;
      }
      else if(%this.throttle > 0.02)
      {
         %this.throttle -= 0.02;
      }
      else
      {
         %this.throttle = 0;
      }
   }
   
   // Steering:
   // if only pressing left and steering isnt to the right
   if((%this.left && !%this.right) && %this.steering <= 0)
   {
      // decrement steering (left)
      %this.steering -= 0.025;
      
      // clamp steering at -1 (full left)
      if(%this.steering < -1)
      {
         %this.steering = -1;
      }
   }
   // if only pressing right and steering isnt to the left
   else if((%this.right && !%this.left) && %this.steering >= 0)
   {
      // increment steering (right)
      %this.steering += 0.025;
      
      // clamp stering at 1 (full right)
      if(%this.steering > 1)
      {
         %this.steering = 1;
      }
   }
   else
   {
      // lower the steering from either direction by 0.02
      // if magnitude of steering is less than 0.02 then set to 0
      if(%this.steering < -0.05)
      {
         %this.steering += 0.05;
      }
      else if(%this.steering > 0.05)
      {
         %this.steering -= 0.05;
      }
      else
      {
         %this.steering = 0;
      }
   }
   
   // 2. calculate longitudinal force
   // get current forward traction force (speed)
   %traction = %this.cTrac * %this.throttle;
   
   // get longitudinal force vector
   // remember that neg Y is up, so we need -cos(rotation) for Y
   %longVelX = mSin(mDegToRad(%this.getRotation())) * %traction;
   %longVelY = -mCos(mDegToRad(%this.getRotation())) * %traction;
   
   // 3. steering / angular vel   
   // adjust steering force:
   // get a base steering force based on speed of movement
   %steerVel = %this.steering * %this.maxSteerAngle;
   
   // 4. apply forces
   // apply linear velocity
   %this.setLinearVelocity(%longVelX SPC %longVelY);
   
   // apply angular velocity
   %this.setAngularVelocity(%steerVel);
   
   // 5. set turret's rotation!
   %this.updateTurret(%this.lastMousePos);
  }


// ____ input functions ____
// gas
function player1Tank::throttleOn(%this)
{
   %this.gasPedal = true;
}
function player1Tank::throttleOff(%this)
{
   %this.gasPedal = false;
}

// brake / reverse
function player1Tank::brakeOn(%this)
{
   %this.brake = true;
}
function player1Tank::brakeOff(%this)
{
   %this.brake = false;
}

// steer left
function player1Tank::leftOn(%this)
{
   %this.left = true;
}
function player1Tank::leftOff(%this)
{
   %this.left = false;
}

// steer right
function player1Tank::rightOn(%this)
{
   %this.right = true;
}
function player1Tank::rightOff(%this)
{
   %this.right = false;
}
#39
12/19/2006 (5:14 pm)
Ok, let's just try a quick fix first. If this doesn't do it, I'll go ahead and work the tutorial myself and then we can compare results.

// create and attach a turret to this tank
function PhysTank::initializeTurret(%this)
{
   // create the new turret and store it on this object
   $turret = new t2dStaticSprite()
   {
      sceneGraph = %this.scenegraph; // make sure to set the scenegraph
      ImageMap = "tankTurretImageMap"; // set the image to use
      config = "PhysTankTurretDatablock"; // set the config datablock
   };
   // mount the turret to the tank and set 'trackRotation' (fourth argument)
   // to false so we can rotate our turret independently
   $turret.fireLinkPoint = $turret.addLinkPoint( "0 -0.20" );
   $turret.mount(%this, "0 -0.1", 0, false, true, true, true);
}

// this is our update turret function to rotate the turret 
// towards the mouse pointer
function PhysTank::updateTurret(%this, %mousePos)
{
   // get a vector from the tank to the mouse pointer
   %vec = t2dVectorSub(%this.getPosition(), %mousePos);
   
   // get the desired angle required to point at the mouse pointer   
   %angle = -mRadToDeg(mATan(getWord(%vec,0),getWord(%vec,1)));
   
   // tell the tank turret to rotate towards that angle at a rate of 
   // 'turret.turnSpeed' which was set in our turret's datablock
   $turret.rotateTo(%angle, $turret.turnSpeed);
}

function shoot(){
      //determine shot angle    
      %tankPosition = $turret.getPosition();
      %mousePosition = sceneWindow2D.getMousePosition();
      %vec = t2dVectorSub(%tankPosition, %mousePosition);
      %shotAngle = -mRadToDeg(mATan(getWord(%vec,0),getWord(%vec,1)));
      $nextScheduledShot = schedule(200, 0, "shoot");
      
     //create the projectile
     %bullet = new t2dStaticSprite(){ scenegraph = sceneWindow2D.getSceneGraph();};
     %bullet.setPosition( $turret.getLinkPoint( %linkPoint ) );
     %bullet.setImageMap ( "smallBubbleImageMap" );
     %bullet.setSize = "4 4";
     %bullet.setLinearVelocityPolar ( %shotAngle, 100);  // shoot at speed 100     
}
#40
12/19/2006 (7:10 pm)
Hi Don it works!
I did it just a little differently, and I mean very little. My link point has a number where you named yours.
Under the //craete the projectile section you have:
%bullet.setPosition( $turret.getLinkPoint( %linkPoint ) );
and I have:
%cannon.setPosition( turret.getlinkpoint("1") ); // bullet starts at the cannon tip


But the bug I worried about where the shooting code is also a rotating object is happening, here is a little entertaining video clip I took of the tank firing in action

www.wickedbig.com/temp/tank_fire_problem.wmv

I apologise for the .wmv file if you are not on a windows computer. For some reason quick time pro will not open .avi files created by Fraps which is what I use to capture video :(