Giving the player fire control over an AITurret
by Kirby Webber · in Torque Game Engine · 12/29/2005 (2:16 pm) · 13 replies
I just recently got Paul Dana's turret resource into 1.4 and working (YAY!).
After that, I went about getting a fully functional AITurret mounted to my vehicles (I'm working with hovers, but that's hardly relevant.)
So far, so good.
At this point, I'm looking for a way to allow the AITurret to pick it's own targets (as it already does), but allow the player to actually fire the turret via the mouse.
Additionally, I'll need to credit the player with any kills which are techically scored by the turret.
Any advice would be greatly appreciated.
~ Cheers.
After that, I went about getting a fully functional AITurret mounted to my vehicles (I'm working with hovers, but that's hardly relevant.)
So far, so good.
At this point, I'm looking for a way to allow the AITurret to pick it's own targets (as it already does), but allow the player to actually fire the turret via the mouse.
Additionally, I'll need to credit the player with any kills which are techically scored by the turret.
Any advice would be greatly appreciated.
~ Cheers.
#2
12/29/2005 (2:40 pm)
Okay, I think I follow your logic here Frank, but how exactly would one go about making the vehicle "aware" of the mounted object in script?
#3
I had planned to use that function to wrench fire control back from the AI, but when it comes down to brass tacks...
12/29/2005 (2:43 pm)
Incidentally, the fucntion that fires when the mouse button is pressed is "MouseFire()" in default.bind.csI had planned to use that function to wrench fire control back from the AI, but when it comes down to brass tacks...
#4
%vehicle.turret = new AITurret() // or whatever you do to create the turret
Now the reference to the turret is stored in that vehicle.
Anytime the vehicle gets a onTrigger event that it recognizes as belonging to the turret then do some on action on the turret:
%vehicle.turret.setImageTrigger(0,1) // something like that, I am not sure what the exact code is trigger an image on another object. You may even just have a 'fire' command for the turret object. You could also do supervisory control: select target, show target, etc.
Also, you do not need to use a high level function like mousefire. There should be a function that gets called on the player/vehicle object server side. Oh yeah, this code needs to be server side in the scripting.
Here is an example of using onTrigger:
This code uses the AIPlayer object as a turret by mounting the AIPlayer onto a staticShape in mount point 1 so that the player can rotate. The AITurret I am using does not correspond to the one developed in C++. It is a script only AITurret based upon AIPlayer. I use this onTrigger function when I mount the AITurret as a Player so that the turret sees my firing. Notice that I check to see that %triggerNum is equal to some value (fire value is 0 for Player). So this code might work on the AITurret stuff you are using. Just see if this onTrigger function gets called by putting in an echo command:
Noitice is the above code I am firing two weapons at the same time.
12/29/2005 (2:59 pm)
Okay, when you actually mount the object to the vehicle do this:%vehicle.turret = new AITurret() // or whatever you do to create the turret
Now the reference to the turret is stored in that vehicle.
Anytime the vehicle gets a onTrigger event that it recognizes as belonging to the turret then do some on action on the turret:
%vehicle.turret.setImageTrigger(0,1) // something like that, I am not sure what the exact code is trigger an image on another object. You may even just have a 'fire' command for the turret object. You could also do supervisory control: select target, show target, etc.
Also, you do not need to use a high level function like mousefire. There should be a function that gets called on the player/vehicle object server side. Oh yeah, this code needs to be server side in the scripting.
Here is an example of using onTrigger:
// catch triggers for player control
function AITurretData::onTrigger(%this, %obj, %triggerNum, %val)
{
// if dead stop firing
if (%obj.getState() $= "Dead")
return;
//echo(%this.getClassName()@" "@%obj.getDatablock().getClassName()@" "@%triggerNum@" "@%val);
if(%triggerNum == 0)
{
%obj.setImageTrigger(%this.weaponSlot[0],%val);
%obj.setImageTrigger(%this.weaponSlot[1],%val);
}
//%obj.setImageTrigger(%this.weaponSlot[0],false);
//%obj.setImageTrigger(%this.weaponSlot[1],false);
}This code uses the AIPlayer object as a turret by mounting the AIPlayer onto a staticShape in mount point 1 so that the player can rotate. The AITurret I am using does not correspond to the one developed in C++. It is a script only AITurret based upon AIPlayer. I use this onTrigger function when I mount the AITurret as a Player so that the turret sees my firing. Notice that I check to see that %triggerNum is equal to some value (fire value is 0 for Player). So this code might work on the AITurret stuff you are using. Just see if this onTrigger function gets called by putting in an echo command:
function <Your Object Datablock Name>::onTrigger(%this, %obj, %triggerNum, %val)
{
echo(%this.getClassName()@" "@%obj.getDatablock().getClassName()@" "@%triggerNum@" "@%val);
}Noitice is the above code I am firing two weapons at the same time.
#5
I have defined my AITurret functions as AITurretData::FunctionName(%args){}
The function (provided with the resource) that handles weapon firing looks like this:
In my HoverVehicle script, I add the turret like so:
The, as an experiment, I tried a simple experiment to access the fireGun() function like so:
Now, obviously (to anyone who understands what's going on... I sure don't =P) nothing happens when you press the trigger, although here's my console output:
What this tells me is that, in my HoverVehicle::OnTrigger() function, Blaster (the turrets name) is recognized as a valid AITurret type or it wouldn't even get to the "attempting to call function fireGun()" would it?
If I'm correct, it's really a matter of syntax then, isn't it?
What would be the proper way to address this specific turret in a way that fireGun() expects?
[edit]
For clarity - I DID try handing %turret adn %obj to the fireGun() function, but the results are vertiably the same.
12/29/2005 (5:31 pm)
Okay... I am slowly getting somewhere. I know this because the console output indicates I'm going down the right path.I have defined my AITurret functions as AITurretData::FunctionName(%args){}
The function (provided with the resource) that handles weapon firing looks like this:
// fire turret gun and setup to fire again fireRate seconds later
function AITurretData::fireGun(%this,%turret)
{
// fire gun
%turret.fire(0);
// setup next fire time
%this.setupNextFire(%turret,%this.fireRate,%this.fireRateVariance);
}In my HoverVehicle script, I add the turret like so:
%turret = new AITurret(Blaster)
{
datablock = blasterTurret;
};
// mount turret at whatever mount point
%obj.mountObject(%turret,9);
%obj.mountedTurret = %turret;The, as an experiment, I tried a simple experiment to access the fireGun() function like so:
function HoverVehicleData::onTrigger(%this, %obj, %turret, %val)
{
Blaster.AITurretData.fireGun(Blaster);
}Now, obviously (to anyone who understands what's going on... I sure don't =P) nothing happens when you press the trigger, although here's my console output:
... Unable to find object: '' attempting to call function 'fireGun'
What this tells me is that, in my HoverVehicle::OnTrigger() function, Blaster (the turrets name) is recognized as a valid AITurret type or it wouldn't even get to the "attempting to call function fireGun()" would it?
If I'm correct, it's really a matter of syntax then, isn't it?
What would be the proper way to address this specific turret in a way that fireGun() expects?
[edit]
For clarity - I DID try handing %turret adn %obj to the fireGun() function, but the results are vertiably the same.
#6
This should be so simple, WTH am I missing? =\
12/29/2005 (5:39 pm)
Nevermind... I tried the whole "Blaster.AITurretData.Firegun()" with some nonsense in place of "Blaster" and it's the same thing.This should be so simple, WTH am I missing? =\
#7
12/29/2005 (6:56 pm)
Okay, you are making good progress. Your on trigger is doing something at least. Now:Blaster.AITurretData.fireGun(Blaster);Is not going to do anything because it does not reference any instance of the objects you are using. Try something along these lines:
// make sure you understand the onTrigger parameters
function HoverVehicleData::onTrigger(%this, %obj, %triggerNum, %val)
{
// check type of trigger, and that it has a positive value
if(%triggerNum == 0 && %val)
{
%obj.mountedTurret.fireGun( %obj.mountedTurret);
}
}Also, put in the echo function to see what triggers cause what. The triggers are NOT just for firing a weapon! There are triggers for other things that will call that function. Like jumping for instance.
#8
Console now says:
Which seems to be a step in the right direction. Obviously, the engine recognized that the entity I'm trying to throw a trigger on was "blaster".
If I may, since you've been so helpful with your time - "teach a man to fish" for a moment.
One of the things I've never quite grasped about Torque script is the passing in of arguments to scriptd functions.
In traditional programming languages, if you do not explicitly hand arguments into a function, those arguments are effectively 'Null'.
In Torquescript it seems that you can grab valid arguments out of 'thin air'. What I believe is actually transpiring is that, as an example, the %obj variable used to instantiate the vehicle in the onAdd() function is being associated with that calss instance, therefore when that variable is called from a hoverVehicleData function, it knows what value to use based upon it's association with that particular instance? *cough*namespcae?*cough*
Am I somewhere on the right track?
If so, the question would then be, how do I associate a function call from one (namespace?) class instance to another instance of an entirely different class?
At any rate, I am extremely grateful for your help thus far Frank.
=)
12/29/2005 (7:37 pm)
Well... this shows some serious progress for me.Console now says:
base.AB/server/scripts/banshee.cs (233): Unknown command fireGun. Object Blaster(1496) AITurret -> Turret -> ShapeBase -> GameBase -> SceneObject -> NetObject -> SimObject
Which seems to be a step in the right direction. Obviously, the engine recognized that the entity I'm trying to throw a trigger on was "blaster".
If I may, since you've been so helpful with your time - "teach a man to fish" for a moment.
One of the things I've never quite grasped about Torque script is the passing in of arguments to scriptd functions.
In traditional programming languages, if you do not explicitly hand arguments into a function, those arguments are effectively 'Null'.
In Torquescript it seems that you can grab valid arguments out of 'thin air'. What I believe is actually transpiring is that, as an example, the %obj variable used to instantiate the vehicle in the onAdd() function is being associated with that calss instance, therefore when that variable is called from a hoverVehicleData function, it knows what value to use based upon it's association with that particular instance? *cough*namespcae?*cough*
Am I somewhere on the right track?
If so, the question would then be, how do I associate a function call from one (namespace?) class instance to another instance of an entirely different class?
At any rate, I am extremely grateful for your help thus far Frank.
=)
#9
I change the AITurretData::fireGun() to AITurret::fireGun() and voila!
Now to tweak and whatnot!
Thanks a million for you help Frank - though if you could answer the question above, it'd be a GREAT help.
~Cheers!
12/29/2005 (7:46 pm)
AHA!I change the AITurretData::fireGun() to AITurret::fireGun() and voila!
Now to tweak and whatnot!
Thanks a million for you help Frank - though if you could answer the question above, it'd be a GREAT help.
~Cheers!
#10
%myObject.someMethod();
to call a member method, it looks at the namespace of %myObject, and calls the method someMethod() within that namespace. For example:
AITurret::fireGun()
{
// do something here
}
%newTurret = new AITurret() {
datablock = myTurretData;
}
%newTurret.fireGun();
TorqueScript knows that since you declared %newTurret (note that this object handle is of local scope, so as soon as you leave this scope you will lose the variable %newTurret) of type AITurret, then when you execute the last line, it searches the AITurret:: namespace for the method.
The reason it worked when you changed the namespace from AITurretData::fireGun() to AITurret::fireGun() is because %obj, when passed into the method is the objectID of the vehicle being driven.
%obj.mountedTurret is the objectID of the turrent object. Alternatively, you could have called it as such:
%obj.mountedTurret.getDataBlock().fireGun(...);
and then the namespace AITurretData would have been used (assuming that was in fact the datablock for your turret.
The only other "magic" variable that floats around is the first parameter sent to a script method...commonly called %this. The engine automatically prepends the objectID of the calling object when it's namespace function is executed as the first parameter--we do not do this ourselves, the engine does it for us, but it's available to us if we desire to use it.
For example, in this case, in your ::fireGun() method, you have two params: %this, and %turret, and you are sending the objectID itself as the user parameter %turret as so:
%obj.mountedTurret.fireGun( %obj.mountedTurret );
--this is actually redundant, because the receiving parameters are %this, and %turret--which are the exact same object. You could just as simply used:
and your call would look like:
%obj.mountedTurret.fireGun();
12/29/2005 (8:42 pm)
You are correct, when you use the format:%myObject.someMethod();
to call a member method, it looks at the namespace of %myObject, and calls the method someMethod() within that namespace. For example:
AITurret::fireGun()
{
// do something here
}
%newTurret = new AITurret() {
datablock = myTurretData;
}
%newTurret.fireGun();
TorqueScript knows that since you declared %newTurret (note that this object handle is of local scope, so as soon as you leave this scope you will lose the variable %newTurret) of type AITurret, then when you execute the last line, it searches the AITurret:: namespace for the method.
The reason it worked when you changed the namespace from AITurretData::fireGun() to AITurret::fireGun() is because %obj, when passed into the method is the objectID of the vehicle being driven.
%obj.mountedTurret is the objectID of the turrent object. Alternatively, you could have called it as such:
%obj.mountedTurret.getDataBlock().fireGun(...);
and then the namespace AITurretData would have been used (assuming that was in fact the datablock for your turret.
The only other "magic" variable that floats around is the first parameter sent to a script method...commonly called %this. The engine automatically prepends the objectID of the calling object when it's namespace function is executed as the first parameter--we do not do this ourselves, the engine does it for us, but it's available to us if we desire to use it.
For example, in this case, in your ::fireGun() method, you have two params: %this, and %turret, and you are sending the objectID itself as the user parameter %turret as so:
%obj.mountedTurret.fireGun( %obj.mountedTurret );
--this is actually redundant, because the receiving parameters are %this, and %turret--which are the exact same object. You could just as simply used:
AITurret::fireGun(%this)
{
// fire gun
%this.fire(0);
// setup next fire time
%this.setupNextFire(%this,%this.fireRate,%this.fireRateVariance);
}and your call would look like:
%obj.mountedTurret.fireGun();
#11
In the end, I realized that despite the fact that the weapon was mounted to an AITurret, I could access the trigger of the weaponImage directly like so:
At any rate, this discussion has proven educational on a number of levels for me! Thanks to you both.
~Off to "catch some fish" =)
12/30/2005 (8:03 am)
Hey Stephen, I really appreciate the clarification... that brigns a great deal of Torque script "out of the fog" for me - so to speak.In the end, I realized that despite the fact that the weapon was mounted to an AITurret, I could access the trigger of the weaponImage directly like so:
function HoverVehicleData::onTrigger(%this, %obj, %triggernum, %val)
{
// check type of trigger, and that it has a positive value
if(%triggerNum == 0 && %val)
{
%obj.mountedTurret.setImageTrigger(0, true);
}
else
%obj.mountedTurret.setImageTrigger(0, false);
}This allows me to focus only on the behavior of the weapon image and not so much on the specifics of the turret datablock with regards to the weaponImage behavior... which is where think Frank was trying to direct me in the first place (I can be pretty thick sometimes).At any rate, this discussion has proven educational on a number of levels for me! Thanks to you both.
~Off to "catch some fish" =)
#12
There are problems with your code as is:
Here are the fixes for you:
Watch out for magic numbers like the zero used for firing you may want to setup some lookup values if there aren't already some defined like this:
Then in your function:
That way if the numbers will be descriptive and can be changed in one location.
Have fun.
12/30/2005 (1:00 pm)
Yes, it is kind of wierd the whole datablock versus object thing. The call does come in on the HoverVehicleData, but it references the object through %obj. I assume the reason it is called on the datablock is because you will get the datablock and through %this and the object through %obj by default.There are problems with your code as is:
function HoverVehicleData::onTrigger(%this, %obj, %triggernum, %val)
{
// check type of trigger, and that it has a positive value
// I structured it this way to work with your function
if(%triggerNum == 0 && %val)
{
%obj.mountedTurret.setImageTrigger(0, true);
}
else
// putting this in the else will cause it to stop firing on other non-firing events like jump
%obj.mountedTurret.setImageTrigger(0, false);
}Here are the fixes for you:
function HoverVehicleData::onTrigger(%this, %obj, %triggernum, %val)
{
// check type of trigger
if(%triggerNum == 0)
{
// Use the %val on the trigger image to determine true or false,
// if you print out the values you will see it gets 1 (true) and 0 (false) on press and release.
%obj.mountedTurret.setImageTrigger(0, %val);
}
else if(%triggerNum == <somenumber>) // note <somenumber> is not code
{
// Also, if you add your own triggers later for different things you can handle them by number here.
// Like fire missiles or something.
}
}Watch out for magic numbers like the zero used for firing you may want to setup some lookup values if there aren't already some defined like this:
// inside the hovervehicledata block ... FireTriggerNum = 0; ...
Then in your function:
// check type of trigger
if(%triggerNum == %this.FireTriggerNum)
{That way if the numbers will be descriptive and can be changed in one location.
Have fun.
#13
12/30/2005 (1:23 pm)
Hey Franks. Thanks agan! =)
Torque Owner Frank Carney
DemolishunConsulting Rocks!