GUI Slider Control to Set Flying Vehicles Engine Variables in Update Forces Function Using Define Engine Methods or Console Methods Causing Problems
by Philip J. Stroh · in Torque Game Engine · 02/04/2011 (7:22 am) · 11 replies
This is the code in my FlyingVehicle class that I call from a server side script:
(Note: This is in Beta 3, however, I was having the same problem in Beta 1 using ConsoleMethods)
--------------
First Example:
--------------
DefineEngineMethod( FlyingVehicle, setMassOfVehicle, void, ( F32 mass ),, "Mass of the flying vehicle")
{
object->setMassOfVehicle( mass );
}
This code, in turn, calls the following function:
void FlyingVehicle::setMassOfVehicle(F32 mass)
{
mDataBlock->mass = mass;
mRigid.mass = mass;
}
---------------
Second Example:
---------------
DefineEngineMethod( FlyingVehicle, setMinDrag, void, ( F32 drag ),, "Linear Drag (eventually slows you down when not thrusting...constant drag)")
{
object->setMinDrag( drag );
}
This code, in turn, calls the following function:
void FlyingVehicle::setMinDrag(F32 drag)
{
mDataBlock->minDrag = drag;
}
I am using a GUI slider control to call this function. In the Gui Slider control I am using the altCommand field so that the DefineEngineMethod function gets called constantly while I am sliding the slider on the control.
Once I move the slider, the client and server versions of my FlyingVehicle object seem to get out of sync. Also, when I move the slider, the animated propeller stops moving and the engine sound is cut off.
These member variables that I am changing, do I need to add them to the packUpdate and unpackUpdate functions? For the datablock variables, do I use packUpdate and unpackUpdate in the FlyingVehicleData class? And, for the FlyingVehicle class member variables, do I use the packUpdate and unpackUpdate from the FlyingVehicle class? What else do I need to do or what am I doing wrong?
(Note: This is in Beta 3, however, I was having the same problem in Beta 1 using ConsoleMethods)
--------------
First Example:
--------------
DefineEngineMethod( FlyingVehicle, setMassOfVehicle, void, ( F32 mass ),, "Mass of the flying vehicle")
{
object->setMassOfVehicle( mass );
}
This code, in turn, calls the following function:
void FlyingVehicle::setMassOfVehicle(F32 mass)
{
mDataBlock->mass = mass;
mRigid.mass = mass;
}
---------------
Second Example:
---------------
DefineEngineMethod( FlyingVehicle, setMinDrag, void, ( F32 drag ),, "Linear Drag (eventually slows you down when not thrusting...constant drag)")
{
object->setMinDrag( drag );
}
This code, in turn, calls the following function:
void FlyingVehicle::setMinDrag(F32 drag)
{
mDataBlock->minDrag = drag;
}
I am using a GUI slider control to call this function. In the Gui Slider control I am using the altCommand field so that the DefineEngineMethod function gets called constantly while I am sliding the slider on the control.
Once I move the slider, the client and server versions of my FlyingVehicle object seem to get out of sync. Also, when I move the slider, the animated propeller stops moving and the engine sound is cut off.
These member variables that I am changing, do I need to add them to the packUpdate and unpackUpdate functions? For the datablock variables, do I use packUpdate and unpackUpdate in the FlyingVehicleData class? And, for the FlyingVehicle class member variables, do I use the packUpdate and unpackUpdate from the FlyingVehicle class? What else do I need to do or what am I doing wrong?
About the author
Companies I worked for: VTSG: F16 simulations. Simigon: Primarily F16 simulations. Veraxx: 2D simulation called the TEN. Valador: DON, plus new Torque projects. I served in U.S. Marine reserves for a little more than 9 years in an infantry unit.
#2
Class member variables can be updated and pushed to the clients in real time. However, they need to be handled in pack/unpackUpdate. If the author of the class in question did not intend for the variable to be changed in real time then you will probably need to modify said network functions to accommodate that.
02/05/2011 (6:22 am)
Datablock values are not designed to be changed once the game is started. Datablocks are sent to the client only on mission load. There is no mechanism in place for Torque to push datablock changes to clients after the mission is loaded. Class member variables can be updated and pushed to the clients in real time. However, they need to be handled in pack/unpackUpdate. If the author of the class in question did not intend for the variable to be changed in real time then you will probably need to modify said network functions to accommodate that.
#3
I changed it so that I am using "DefineEngineMethod" to change "member variables" instead of "datablock variables" that are used in the updateForces() function in the FlyingVehicle class.
Also, I am reading and writing the F32 member variables in pack/unpackUpdate and that seems to be working fine.
Before I mount the WrightFlyer, I put a breakpoint in the "StdClientProcessList::advanceTime" function on the line with the following code, "obj->advanceTime( dt );" <-- This is in the loop where it is advancing the time of all the objects in the ProcessList. One of the objects it finds at this point is the "FlyingVehicle" object.
When I mount the FlyingVehicle object in the game, then when I hit that same breakpoint, the "FlyingVehicle" is still in the ProcessList, but is now also attached to mControlObject in the "Player" class as well via a weak refernce pointer.
So far everything is working fine.
Now, when I change a member variable of the FlyingVehicle using the GUI slider control, the member variable changes and everything is still fine.
However, now when I press "w" to move the aircraft forward, the engine sound stops and the propeller animation stops.
At this point, the FlyingVehicle is no longer in the ProcessList, however it is still attached to the mControlObject in the Player object.
The engine sound is called in the advanceTime function in the FlyingVehicle class, but because the FlyingVehicle is no longer part of the ProcessList, it can no longer call the function, "obj->advanceTime( dt );". Therefore, the engine sound stops working.
I am not sure what I am doing wrong. Once the WrightFlyer is mounted, how do I access the advanceTime() function of the WrightFlyer? Or better yet, why is the FlyingVehicle object being removed from the ProcessList?
One thing that is weird, is that if I fly back to the location where I made the member variable change in the first place, I can hear the engine sound. It's almost as if a version of the WrightFlyer exists stationary at that location, invisible, and making engine sounds, and the one that I am using (and visible) is the one attached to the player.
02/05/2011 (11:44 am)
Thanks for the reply Scott.I changed it so that I am using "DefineEngineMethod" to change "member variables" instead of "datablock variables" that are used in the updateForces() function in the FlyingVehicle class.
Also, I am reading and writing the F32 member variables in pack/unpackUpdate and that seems to be working fine.
Before I mount the WrightFlyer, I put a breakpoint in the "StdClientProcessList::advanceTime" function on the line with the following code, "obj->advanceTime( dt );" <-- This is in the loop where it is advancing the time of all the objects in the ProcessList. One of the objects it finds at this point is the "FlyingVehicle" object.
When I mount the FlyingVehicle object in the game, then when I hit that same breakpoint, the "FlyingVehicle" is still in the ProcessList, but is now also attached to mControlObject in the "Player" class as well via a weak refernce pointer.
So far everything is working fine.
Now, when I change a member variable of the FlyingVehicle using the GUI slider control, the member variable changes and everything is still fine.
However, now when I press "w" to move the aircraft forward, the engine sound stops and the propeller animation stops.
At this point, the FlyingVehicle is no longer in the ProcessList, however it is still attached to the mControlObject in the Player object.
The engine sound is called in the advanceTime function in the FlyingVehicle class, but because the FlyingVehicle is no longer part of the ProcessList, it can no longer call the function, "obj->advanceTime( dt );". Therefore, the engine sound stops working.
I am not sure what I am doing wrong. Once the WrightFlyer is mounted, how do I access the advanceTime() function of the WrightFlyer? Or better yet, why is the FlyingVehicle object being removed from the ProcessList?
One thing that is weird, is that if I fly back to the location where I made the member variable change in the first place, I can hear the engine sound. It's almost as if a version of the WrightFlyer exists stationary at that location, invisible, and making engine sounds, and the one that I am using (and visible) is the one attached to the player.
#4
Sounds like perhaps you are not mounting the objects properly. Are you mounting the Player to the Flyer or the Flyer to the Player?
02/05/2011 (2:24 pm)
It makes sense that you can still hear the engine sound at the original location if advanceTime is not being called. It's not because there is an invisible version of the flyer, it's just because the sound still exists in the audio system and it has not been stopped or moved. (It's the advanceTime function that is responsible for updating the position of the sound source as the vehicle moves.)Sounds like perhaps you are not mounting the objects properly. Are you mounting the Player to the Flyer or the Flyer to the Player?
#5
Here is the code I use to mount the player to the FlyingVehicle:
Here is the server command code:
Then, I am calling the following server code:
02/05/2011 (2:41 pm)
Thanks for clearing that up.Here is the code I use to mount the player to the FlyingVehicle:
function PlayGui::onMouseDown(%this, %pos, %start, %ray)
{
%ray = VectorScale(%ray, 1000);
%end = VectorAdd(%start, %ray);
%searchMasks = $TypeMasks::VehicleObjectType;
// Search!
%scanTarg = ContainerRayCast( %start, %end, %searchMasks );
%client = LocalClientConnection.getId();
// Get our player/actor
%player = LocalClientConnection.player;
// If an enemy AI object was found in the scan
if( %scanTarg )
{
// Get the enemy ID
%target = firstWord(%scanTarg);
// Don't shoot at yourself
if( %target != %player )
{
%node = 0;
%vehicle = %target;
commandToServer('MountPlayer', %vehicle, %player);
flightControlCTRL.setVisible(1);
schedule(100, 0, "updateFlightParametersDisplay", %vehicle, true);
if(Canvas.isCursorOn())
hideCursor();
else
{
showCursor();
PlayGui.setFirstResponder();
}
SetFlightParametersDisplay(%vehicle, true);
return;
}
}
else
{
PlayGui.setFirstResponder();
}
}Here is the server command code:
function serverCmdMountPlayer(%client, %vehicle, %player)
{
%vehicle.getDatablock().mountPlayer(%vehicle, %player);
}Then, I am calling the following server code:
function VehicleData::mountPlayer(%this, %vehicle, %player)
{
//echo("\c4VehicleData::mountPlayer("@ %this.getName() @", "@ %vehicle @", "@ %player.client.nameBase @")");
if (isObject(%vehicle) && %vehicle.getDamageState() !$= "Destroyed")
{
%player.startFade(1000, 0, true);
%this.schedule(1000, "setMountVehicle", %vehicle, %player);
%player.schedule(1500, "setAllMeshesHidden", true);
}
}
function VehicleData::setMountVehicle(%this, %vehicle, %player)
{
//echo("\c4VehicleData::setMountVehicle("@ %this.getName() @", "@ %vehicle @", "@ %player.client.nameBase @")");
if (isObject(%vehicle) && %vehicle.getDamageState() !$= "Destroyed")
{
%node = %this.findEmptySeat(%vehicle, %player);
if (%node >= 0)
{
//echo("\c4Mount Node: "@ %node);
%vehicle.mountObject(%player, %node);
//%player.playAudio(0, MountVehicleSound);
%player.mVehicle = %vehicle;
}
}
}
function VehicleData::findEmptySeat(%this, %vehicle, %player)
{
//echo("\c4This vehicle has "@ %this.numMountPoints @" mount points.");
for (%i = 0; %i < %this.numMountPoints; %i++)
{
%node = %vehicle.getMountNodeObject(%i);
if (%node == 0)
return %i;
}
return -1;
}
#6
Where you're running into trouble is in passing that client ghost reference directly to the server and trying to perform a mount. See, the commandToServer function has no mechanism for translating that vehicle reference from a client ghost to a server object (It cannot, since Torquescript is typeless so there is no difference between a reference, which is just an ID number, and a plain old number.)
Follow? The client ghost is a copy, a separate instance with an entirely different ID. That code would not work at all in a real network situation. However, when you run the game locally your client and server exist within the same process and have access to the same global object pool. The server can "see" the client objects, which is why the code does not fail... But nor does it function properly because Torque still maintains the concept of client/server.
Too much information? I tend to get carried away..
What you need to do is use getGhostID and resolveObjectFromGhostIndex to find the server-side object to which that ghost belongs. getGhostID will get the ghost-index of an object, which is an index number that both the client and server share. resolveObjectFromGhostIndex can then be used to turn that ghost-index back into a proper object ID/reference on the server side. So your commandToServer should look like this:
Note there is no need to pass the Player, since the server can find that from the NetConnection. Like so:
See if that doesn't work a bit better.
Alternately, you could send the ray start and end to the server instead, and do the ray cast there. That would work too.
02/05/2011 (5:03 pm)
Aha! I see your problem: You are trying to mount two client-side objects. Mounting must be done on the server. Your ray cast is being done client-side (because PlayGui is viewing the client scenegraph) (EDIT NOTE: That last statement is wrong, see next post). Every object you see is a client-side copy called a ghost. The vehicle object you're finding with that ray cast is the client ghost of the vehicle in question. Where you're running into trouble is in passing that client ghost reference directly to the server and trying to perform a mount. See, the commandToServer function has no mechanism for translating that vehicle reference from a client ghost to a server object (It cannot, since Torquescript is typeless so there is no difference between a reference, which is just an ID number, and a plain old number.)
Follow? The client ghost is a copy, a separate instance with an entirely different ID. That code would not work at all in a real network situation. However, when you run the game locally your client and server exist within the same process and have access to the same global object pool. The server can "see" the client objects, which is why the code does not fail... But nor does it function properly because Torque still maintains the concept of client/server.
Too much information? I tend to get carried away..
What you need to do is use getGhostID and resolveObjectFromGhostIndex to find the server-side object to which that ghost belongs. getGhostID will get the ghost-index of an object, which is an index number that both the client and server share. resolveObjectFromGhostIndex can then be used to turn that ghost-index back into a proper object ID/reference on the server side. So your commandToServer should look like this:
commandToServer('MountPlayer', %vehicle.getGhostID());Note there is no need to pass the Player, since the server can find that from the NetConnection. Like so:
function serverCmdMountPlayer(%client, %ghost)
{
%vehicle = %client.resolveObjectFromGhostIndex(%ghost);
%player = %client.player;
// Might want to add "if (isObject(%vehicle) && isObject(%player))"
%vehicle.getDatablock().mountPlayer(%vehicle, %player);
}See if that doesn't work a bit better.
Alternately, you could send the ray start and end to the server instead, and do the ray cast there. That would work too.
#7
02/05/2011 (5:07 pm)
Actually... wait a minute. Now that I think about it, ContainerRayCast only operates on the server container.. so while it's true that code would not work in a real network situation, I was wrong about why. ... Bugger, because now I'm not sure why that code is not working in a local game instance. The vehicle reference you're getting from that should in fact be the server object.
#8
The vehicle object returned from your ray cast in your code would be a server object. HOWEVER, the player reference you are passing in commandToServer IS NOT! So what happens is that you wind up mounting the client Player ghost to the server Vehicle instance!
Solution: Use the alternative suggestion I posted. Send the ray start and end, and do the ray cast on the server (since you cannot do a ContainerRayCast on the client). But also: Do not pass the %player id in the commandToServer. Instead use the server-side player which can be found as I previously suggested. Something like this:
Sorry for the confusion. Edited for multiple typing errors. What can I say, I'm tired.
02/05/2011 (5:16 pm)
Ah. Ok. Figured it out. (I say for the second time, take with a grain of salt :P )The vehicle object returned from your ray cast in your code would be a server object. HOWEVER, the player reference you are passing in commandToServer IS NOT! So what happens is that you wind up mounting the client Player ghost to the server Vehicle instance!
Solution: Use the alternative suggestion I posted. Send the ray start and end, and do the ray cast on the server (since you cannot do a ContainerRayCast on the client). But also: Do not pass the %player id in the commandToServer. Instead use the server-side player which can be found as I previously suggested. Something like this:
function serverCmdMountPlayer(%client, %rayStart, %rayEnd)
{
// Do ContainerRayCast to find vehicle..
...
%player = %client.player;
if (isObject(%vehicle) && isObject(%player))
%vehicle.getDatablock().mountPlayer(%vehicle, %player);
}Sorry for the confusion. Edited for multiple typing errors. What can I say, I'm tired.
#9
I'm relatively new to Torque and this helps me understand the system a lot better.
Have a great rest of your weekend!
02/05/2011 (7:47 pm)
Your the man Scott! Thanks for taking the time to help me with this problem. I really appreciate it.I'm relatively new to Torque and this helps me understand the system a lot better.
Have a great rest of your weekend!
#10
02/05/2011 (8:16 pm)
Happy to help
#11
It turns out that this did not solve my problem, but I do appreciate you helping me understand Torque 3D better:)
It turns out that the fix for my problem here is in the following two threads:
1. www.garagegames.com/community/forums/viewthread/123439/
and
2. www.garagegames.com/community/forums/viewthread/111049
02/06/2011 (11:40 am)
@Scott: Once again thanks for setting me straight on the mounting of the flyer.It turns out that this did not solve my problem, but I do appreciate you helping me understand Torque 3D better:)
It turns out that the fix for my problem here is in the following two threads:
1. www.garagegames.com/community/forums/viewthread/123439/
and
2. www.garagegames.com/community/forums/viewthread/111049
Torque 3D Owner Philip J. Stroh
Valador Inc
I have a Wright Flyer plane sitting at an airport. I click on the Wright Flyer with my cursor and mount it with my player. Once my player is mounted, a GUI appears that allows me to change FlyingVehicle datablock variables or FlyingVehicle member variables on the fly using Gui slider controls.