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.
Page «Previous 1 2 3 Last »
#1
12/13/2006 (3:07 pm)
I am either not close at all or its so obviousethat I have no responses yet :)
#2
12/13/2006 (4:21 pm)
The reason the bullet is starting in the center is because you never set the position of the bullet. You just apply an impulse, and the default position will be 0,0 - the center of the screen. Does it always move in the same direction, or does it appear that the angle is calculated correctly?

I would put in some echo statements to see what the values of your positions and the calculated angle are.

The error doesn't have anything to do with the bullet. From the looks of it, I would say it can't find the bitmap in the datablock there. Is it showing up even though you are getting the error?

Did you write the datablock from hand, or was it created in the editor?
#3
12/13/2006 (6:05 pm)
Yes the bullets are always in the center and shoot straightup (to the top). I can drive the tank around and the turret turn towards the mouse. I "stole" the datablock from the resource.cs and pasted it in the PhysTank.cs.

I will try some echo statements and review some of the other tutorials and see what else I can find. Thanks for the help/ideas.
#4
12/13/2006 (7:19 pm)
I agree with Eric on the current error you have, it looks like it can not find the bitmap.

I get a different error when I run the script using your code when I hit the fire button. I did make one change to your shooter code, I commented out:
$nextScheduledShot = schedule(500, 0, "shoot");
so it would only fire once per space push. To call the function I added in:
moveMap.bindCmd(keyboard, "space", "shoot();", "");

It fires exactly as you described, from the center of the turret and always goes up when ideally you want it from the tip of the barrel and in the pointing direction. When I check I see this error after each shot:

resources /Tank/PhysTank.cs (247): Unable to find object: 'turret' attempting to call function 'getPosition'
t2dAngleBetween() - Invalid number of parameters!

This I am assuming the error is comming from this line:
%tankPosition = turret.getposition();

Turret is defined in the code that comes with the tank physics which you can take a peek at here incase anyone else wants in on this :)
tdn.garagegames.com/wiki/TGB/MiniTutorials/SimpleTankPhysics


So I am guessing correctly, the big question is what would be the correct object to use if turret is not correct? And will resolve the problem for direction, but not where the shot originates from?
#5
12/13/2006 (8:44 pm)
Like I said in my previous post, you have to set the position of the %bullet, to the position of the "turret". If the turret object you are currently trying to get position from is not the correct object, then as Jonothan said, you have to find out which object is correct. Once you have done that, then you need to call setPosition on the %bullet and pass in the position of the turret. That will cause the bullet to start from the position of the turret.

Now as for getting the correct angle to apply the velocity, that is what the echos are for.

As for the datablock issue, since you simply grabbed it from the resources, then you probably want to change the

imageName = "./images/tankChassis";

line to something like:

imageName = "~/<projectfolder>/data/images/tankChassis";

Replacing with the appropriate name of the project of course. Then just make sure the image is in that directory...

If you can't get the angle to work correctly, then post the output of the echo calls, and I'll see what I can find out...
#6
12/13/2006 (8:50 pm)
Awesome thanks guys, I'll start looking into it!
#7
12/13/2006 (9:46 pm)
Ahh
%bullet.setPosition( %tankPosition );

Now the bullets move with the tank, but still shoot straight up. The tank still drives around fine and the turret is still moving towards the mouse, just not shooting at..yet.
#8
12/14/2006 (6:45 am)
One down at least...

Like I said, just echo out the value of each variable at each step, then post what you are getting here...

One other thing to check is what you are getting out of the angle between call, and what the setLinearVeloticy call is expecting. In other words, is one giving you the angle in degrees, but the other is expecting radians?

I don't remember off the top of my head, and I think TGB is pretty good with giving and expecting values in the same format, but it's something to check...
#9
12/14/2006 (11:51 am)
NUTS! I am curious if you made any other changes besides %bullet.setPosition I tried that with the current fire code and nothing changed for me.
#10
12/14/2006 (7:52 pm)
Nope, here is my .cs. Coders may want to avert there eyes. Well, most of it.

/ 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 = "15"; // coefficient of traction (max speed)
   size = "8.120 8.120"; // default size
};

// Turret Datablock definition
datablock t2dSceneObjectDatablock(PhysTankTurretDatablock)
{
   name = "turret";
   class = "PhysTankTurret";
   turnSpeed = "80"; // 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
   };

   // 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() {
   %tankPosition = turret.getposition();    //turret.getposition()
   %mousePosition = sceneWindow2D.getMousePosition();
   
   %shotAngle = t2dAngleBetween(%tankPosition, %mousePosition);
   %bullet = new t2dStaticSprite(){ scenegraph = sceneWindow2D.getSceneGraph();};
   %bullet.setPosition( %tankPosition );
   %bullet.setImageMap ( "ggLogoImageMap" );
   %bullet.setSize = "8 8";
   %bullet.setLinearVelocityPolar ( $shotAngle, 100); // shoot at speed 100
   // shedule the next sho in 500 mili
   $nextScheduledShot = schedule(500, 0, "shoot");
}


// PhysTank onTimer callback
function PhysTank::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;
      }
   }
#11
12/14/2006 (8:05 pm)
I just found the error in your function...

When you calculate the angle, you do it thus:

%shotAngle = t2dAngleBetween(%tankPosition, %mousePosition);

But then when you set the angle of the bullet:

%bullet.setLinearVelocityPolar ( $shotAngle, 100); // shoot at speed 100

See the error? You set the angle to a local variable %shotAngle, but then set it as a global $shotAngle. There is no global shot angle, so that will be 0 degrees, or straight up...
#12
12/14/2006 (8:29 pm)
Ok that helps, ALOT. The bullets now shoot from all direction around the tank. There not really shooting at the mouse though. Now I have to look into what you/Eric were talking about in your post above about .setLinearVelocity and what it expects. I'll hit that tomorrow, now I want to drive around and watch my bullets fly around :).
#13
12/15/2006 (8:52 pm)
Ok, after looking at this thread:
http://www.garagegames.com/mg/forums/result.thread.php?qt=44951
I was able to use their code and make it work with the tank physics package. So I am using the stock tank physics with only the two changes to phystank.cs so I am not using the mouse to call this function, I just simply assigned it to the space bar so to fire I have this:
moveMap.bindCmd(keyboard, "space", "shootCannon();", "");


Now for the fun part, the bit that makes the tank go boom boom! :)

function shootCannon() {
   // determine shot angle
   %tankPosition = MyTank.getPosition();
   %mousePosition = sceneWindow2D.getMousePosition();
    %difference = t2dVectorSub(%mousePosition, %tankPosition);
   %dx = getWord(%difference, 0); 
   %dy = getWord(%difference, 1);
   %shotAngle = mRadToDeg(mAtan(%dx, -%dy));

   // create the projectile (bullet)
   %cannon = new t2dStaticSprite() { scenegraph = sceneWindow2D.getSceneGraph(); };
   %cannon.setPosition( %tankPosition ); // bullet starts at the ship
   %cannon.setImageMap( "particles3ImageMap" );
   %cannon.setSize = "8 8";
   %cannonPower = MyTank.cannonPower;
   %cannon.setLinearVelocityPolar( %shotAngle, 100 );	// shoot at speed 100		
	   
}


You will find the turret tracks and fires in the direction of your mouse even if your tank is on the move. However it does not look very nice. The shot is comming from the middle of your tank and not the barrel, the other problem is the barrel tracks a little slow which looks nice, but the shot tracks faster. So if you fire before the barrel lines up where your mouse is located because it is still rotating it looks very bad because the shot is coming out of the side of the turret.
I do not know what could be done so that the cannon only fires in a line from the barrel where it currently points and not where your mouse is. It would involve changing %mouseposition in %difference = t2dVectorSub(%mousePosition, %tankPosition); to something else I think becasue we really do not care about the mouse position at this point, only the turret rotator does, we want to know where that turret is pointing and use that for the in the %difference line, then it should look nicer when it fires.
#14
12/15/2006 (10:00 pm)
Awesome, thanks for the idea. I kept asking myself why can't I just use the turret information. Guess I can:

function shoot(){
   //determine shot angle
   %tankPosition = turret.getPosition();
   %mousePosition = sceneWindow2D.getMousePosition();
   %vec = t2dVectorSub(%tankPosition, %mousePosition);
   %shotAngle = -mRadToDeg(mATan(getWord(%vec,0),getWord(%vec,1)));
   
    %bullet = new t2dStaticSprite(){ scenegraph = sceneWindow2D.getSceneGraph();};
     %bullet.setPosition( %tankPosition );
     %bullet.setImageMap ( "ggLogoImageMap" );
     %bullet.setSize = "8 8";
    %bullet.setLinearVelocityPolar ( %shotAngle, 100);  // shoot at speed 100
   // shedule the next sho in 500 mili
    $nextScheduledShot = schedule(500, 0, "shoot");
}

Works really well, but is faster than the current turrent speed.

Yes I think the next project is to get the bullet to come out of the tank turret. Time to google! Of course not till I drive around and shoot for a bit. :)
#15
12/16/2006 (4:32 pm)
I am hoping maybe someone here maybe might shine some light on the fire direction and the shot origin problem, I find it hard to believe that something similar has not been done before in TGE or RGB. And on an image I am about to mention, I would like to have three additional points where shots can originate from besides the cannon.

So, if you want a new tank image or just a second one, here is one from my project I am working on you can use.
www.wickedbig.com/temp/tankChassis.pngwww.wickedbig.com/temp/tankTurret.png
#16
12/16/2006 (7:30 pm)
Wow thanks, that is a really nice tank (and offer). I remember looking awhile back, I found some information about it looking up "muzzle flash". I'll do some more looking tonight.
#17
12/16/2006 (8:12 pm)
I have to confess to not having gone through the tutorial you mention, but maybe sharing what's been working for me will help you.

The function that creates my turret has a line where I give the projectile sprite a pont to fire from with this variable:

%turret.fireLinkPoint = %turret.addLinkPoint( "0 -0.7" );

One thing to note for Jonothan's post is that you can assign multiple link points to an object. I have an eneny turret that uses 2, and my player has used up to 4 in the past. With some clever variable use, you can change these up with key presses very easily. I keep it simple and just append a number as required (%turret.fireLinkPointOne, %turret.fireLinkPointTwo, etc. )


When the turret actually shoots, I get that same variable for my setPosition call for the projectile sprite. I also modify the projectile rotation based on %instigator - which is the originating object, in this case the turret. Finally I adjust the projectile speed based on the %instigator object's speed as well.

%projectile.setPosition( %instigator.getLinkPoint(%fireLinkPoint ) );
    %projectile.setRotation( %instigator.getRotation() );
    %projectile.setLinearVelocityPolar(  %instigator.getRotation(),  t2dVectorLength( %instigator.getLinearVelocity() ) + %speed );

I should point out that variable names in this function may look a little confusing since they don't match the name %turret from the first code block. That's because it's actually from a generic shoot function that all entities can use. For example, if I had multiple tanks in the game that all use .50 cal machine guns they would all call this same function with this line:

fire50CalMG( %turret, %turret.fireLinkPoint );

I hope that helps some. If it doesn't make sense, I've got the thread flagged for notify, so ask away!

- Don
#18
12/16/2006 (9:59 pm)
Hi Don,
Thank you for the information! I am way too sleepy to dare to try it right now, but I think the generic code will be ok and I can not wait to give it a whirl tomorrow!
#19
12/17/2006 (9:17 am)
@Don

Thanks for the help. Just tried it a few times

My current code

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( %tankPosition );
     %bullet.setImageMap ( "gglogoImageMap" );
     %bullet.setSize = "4 4";
     %bullet.setLinearVelocityPolar ( %shotAngle, 100);  // shoot at speed 100
   // shedule the next sho in 500 mili
   
     %tankPosition.fireLinkPoint = %tankPosition.addLinkPoint( "0 -0.7" );
   
}

I tried %turret.fireLinkPoint as well. My console error is: Tanks/gameScripts/PhysTank.cs (163) : Unable to find object: '2.992000 15.940000' attempting to call function 'addLinkPoint' thats when I'm using %tankPosition, when I use %turret there are no numbers. Nice part is that when I drive around the numbers move, so thats good :). Ever have the feeling your missing something very obviouse? Wish I knew more about echo, probably find it with that.

Should I have a datablock for the projectile? like
datablock ProjectileData (defaultProjectile) {
projectileshapename = "yourfile';
sizScale = 0.4;
velocity = 83;
//add more info if needed

};
#20
12/17/2006 (9:51 am)
Ok, I see a couple of things real quick to clarify.

In your line of code above you're missing a '%' in front of the variable. It should be
//determine shot angle
      %tankPosition = %turret.getPosition();
But that's a local variable, so that's why you get Unable to find object:

Second, the link point needs to be added when you first make the tank. Placing at the end of the fire block makes it the very last thing the code tries, and so again it's not going to be able to find the object you're trying to give a ilnk point.

I wanted to go ahead and reply, but right now I actually have some free time so I'm going to look more carefully at your example code and then reply again.

- Don
Page «Previous 1 2 3 Last »