Firing trigger madness
by Lee Latham · in Torque Game Engine · 04/03/2008 (5:39 pm) · 5 replies
Can someone please explain to me the relationship between the setImageTrigger on the server and the $mvTriggerCount variables on the client? I have long looked but never found a complete explanation of the system. I try various things but it never seems to work as I expect.
I have a fair amount of trigger-related weirdness that is coming to a head, and I just have never grokked, really, how the system works. I have odd things that happen when a trigger is depressed in conjunction with other events, and such as that, and so I figure I need to finally get this thrashed out in my understanding to get control of the situation.
Any help would be greatly appreciated!
I have a fair amount of trigger-related weirdness that is coming to a head, and I just have never grokked, really, how the system works. I have odd things that happen when a trigger is depressed in conjunction with other events, and such as that, and so I figure I need to finally get this thrashed out in my understanding to get control of the situation.
Any help would be greatly appreciated!
#2
04/03/2008 (11:09 pm)
Extra info - I think there are six trigger slots, 0-5. Slots 0 and 1, as you can see above, set the trigger states o images mounted in slots 0 and 1. Trigger 2 is, I think, used for jumping. The others are for you to use.
#3
I also understand my own issues a good deal more clearly. A couple of questions:
1. So when I do %obj.setImageTrigger(0,true) on the server, I am merely incrementing $mvTriggerCount0 on the client side, which is in turn gets sent back to the server during that every 32ms update? I've never been clear on quite what that does. I apologize if the answer to that particular question is in your explanation, I read it as carefully as I could.
2. Another thing that confuses me is how jumping is included in this system. I'm not aware of any state setup for jumping in any player related datablock. Why is this, and/or what is the "natural" way of using this to one's advantage? When I study the stock jump it doesn't seem to be doing any fancy state machine type stuff.
3. There seem to be some special cases where the engine acts differently when the trigger is being continuously fired--either holding the mouse button down, or space bar when jumping. For example, if you're falling and hold down the jump button, you get some extra "zaz" when you land. Or, I've noticed that when I'm firing continuously and try to change weapons, things don't go exactly as expected. Is this also handled on the script side of life, or is there a way for me to understand what is happening differently, there?
edit: in #3 I'm referring specifically to a problem I posted about here: www.garagegames.com/mg/forums/result.thread.php?qt=73826
Another example I have is when mounting a vehicle with the trigger depressed, the vehicle begins firing constantly, and the player can't make it move. Weird stuff, and obviously I need to grok this trigger stuff to smooth out these wrinkles. So I guess the "held down" trigger is what's confusing me the most, personally.
04/04/2008 (8:56 am)
Wow, what an awesome explanation, Stephen, thanks! I've bookmarked this thread for eternal reference. I do understand the system significantly better than before--I owe you a couple beers. :-)I also understand my own issues a good deal more clearly. A couple of questions:
1. So when I do %obj.setImageTrigger(0,true) on the server, I am merely incrementing $mvTriggerCount0 on the client side, which is in turn gets sent back to the server during that every 32ms update? I've never been clear on quite what that does. I apologize if the answer to that particular question is in your explanation, I read it as carefully as I could.
2. Another thing that confuses me is how jumping is included in this system. I'm not aware of any state setup for jumping in any player related datablock. Why is this, and/or what is the "natural" way of using this to one's advantage? When I study the stock jump it doesn't seem to be doing any fancy state machine type stuff.
3. There seem to be some special cases where the engine acts differently when the trigger is being continuously fired--either holding the mouse button down, or space bar when jumping. For example, if you're falling and hold down the jump button, you get some extra "zaz" when you land. Or, I've noticed that when I'm firing continuously and try to change weapons, things don't go exactly as expected. Is this also handled on the script side of life, or is there a way for me to understand what is happening differently, there?
edit: in #3 I'm referring specifically to a problem I posted about here: www.garagegames.com/mg/forums/result.thread.php?qt=73826
Another example I have is when mounting a vehicle with the trigger depressed, the vehicle begins firing constantly, and the player can't make it move. Weird stuff, and obviously I need to grok this trigger stuff to smooth out these wrinkles. So I guess the "held down" trigger is what's confusing me the most, personally.
#4
On the client, when the move is sent to the server, the client's control object processes that move. In player.cc, un updateMove, you see that setImageTriggerState(0,move->trigger[0]); is being called. This sets the image trigger state based on the trigger state in the move.
On the server, when you call setImageTrigger, that console method aso calls setImageTriggerState. So it's just two paths to arrive at the same conclusion.
When you hit space, $mvTriggerCount2 is incremented. When a Player recieves a move in which move->trigger[2] is set to true, he jumps.
Sorry if this is all confusing. Stephen can probably do better ;)
I don't know abou the problems you mention, though - I'd guess it's script troubles, but that's a completely uneducated guess.
04/05/2008 (5:07 am)
Quote:1. So when I do %obj.setImageTrigger(0,true) on the server, I am merely incrementing $mvTriggerCount0 on the client side, which is in turn gets sent back to the server during that every 32ms update? I've never been clear on quite what that does. I apologize if the answer to that particular question is in your explanation, I read it as carefully as I could.That's the obvious conclusion, but that's now how it's working. ShapeBase's console method setImageTrigger sets the trigger state of a mounted image directly. This is a case of having two inputs - the client-side input, which is caused by the player clicking the mouse, and the server-side input, which is caused when %obj.setImageTrigger is called.
On the client, when the move is sent to the server, the client's control object processes that move. In player.cc, un updateMove, you see that setImageTriggerState(0,move->trigger[0]); is being called. This sets the image trigger state based on the trigger state in the move.
On the server, when you call setImageTrigger, that console method aso calls setImageTriggerState. So it's just two paths to arrive at the same conclusion.
Quote:2. Another thing that confuses me is how jumping is included in this system. I'm not aware of any state setup for jumping in any player related datablock. Why is this, and/or what is the "natural" way of using this to one's advantage? When I study the stock jump it doesn't seem to be doing any fancy state machine type stuff.'Triggers' in the move struct that is sent to the server have nothing to do with the state system, except that they share a name. It's quite confusing, actually :P. But basically, the $mvTriggerCounts and move->trigger[n] are simply a bunch of boolean values. You can use them for whatever you want.
When you hit space, $mvTriggerCount2 is incremented. When a Player recieves a move in which move->trigger[2] is set to true, he jumps.
Sorry if this is all confusing. Stephen can probably do better ;)
I don't know abou the problems you mention, though - I'd guess it's script troubles, but that's a completely uneducated guess.
#5
04/07/2008 (3:52 pm)
Iiii see, thanks Daniel!
Torque 3D Owner Stephen Zepp
ShapeBaseImage Overview
The concept of weapons in Torque is pretty involved, and can best be described as a scriptable Finite State Machine (FSM), that allows you to "program" a weapon to move through as many "states" as you like based on several types of factors, including user interaction (triggering), time passing (state timeouts), certain state conditionals (having ammo, etc.), and other simulation based events (callbacks, such as ::onFire handler scripts)
The core functionality of a weapon resides in the Torque class called ShapeBaseImage. As a general rule, developers think of "images" as simply textures, or things that display on the screen, but in this special case, a ShapeBaseImageData defines the states of our weapon, and how to react to all the different things that can happen.
Crossbow.cs is the core example regarding how ShapeBaseImage work, and deserves extensive study.
Additionally, there is a pretty good table of the various state transitions that are available on TDN in the ShapeBaseImageData section, about 1/3 of the total page length down.
You should focus on a few key areas:
Image definitions and states:
datablock ShapeBaseImageData(CrossbowImage)
{
...
}
The script "callback" for when a trigger is received:
function CrossbowImage::onFire(%this, %obj, %slot)
{
...
}
Event Flows
Basically, the operation of a ShapeBaseImage comes in a couple of sections:
Client:
--on the client, we have two primary functions to perform: handling input, and displaying the results of our weapon.
Handing Input
Since Torque's input system is meant to be "modded" (allowed to be changed by the game's player), we provide a hook for TorqueScript to be able to modify how input is handled, called MoveMaps, which allows for basic modding of the input event handling.
To allow TorqueScript to send data to the server however, we have to tie in various TorqueScript variables to the networking system, and we do this via the Move struct, which is sent from the client to the server every 32 milliseconds (it's not a coincidence that this is the same rate as our fixed tick physics, since this Move struct is passed directly to the processTick() on the server for the control object when it arrives at the server).
Within the Move struct, we have several variables that act to consolidate all of the various inputs a player has made within the last 32 ms since the move struct was accumulated. This includes things like mouse pitch/yaw, move speed changes, and other input events, including moveTriggers--which are what is important to us. Effectively, the global variables $mvTriggerCount0, $mvTriggerCount1, and $mvTriggerCount2 (the only three that are used in stock) are mapped via TorqueScript to values within the Move struct.
A full list of the TorqueScript to Move struct variable mappings can be found in the C++ method MoveManager::init(). In TGEA, this method is in the file moveManager.cpp. It may be elsewhere in TGE, but a quick "Find in Files" on the word mvTriggerCount will locate it for you within your project.
As mentioned above, the move struct is shipped to the server on a continuous basis, every 32 milliseconds.
Server
When a move struct is received from a client, it is passed directly to the ::processTick() method of that client's current Control Object. (Note: there can be only one control object per client in stock Torque). Once ::processTick() does some work, it passes this move struct (modified in some cases) off to Player::updateMove(). In the first few lines of ::updateMove(..), we see the following:
// Trigger images if (mDamageState == Enabled) { setImageTriggerState(0,move->trigger[0]); setImageTriggerState(1,move->trigger[1]); }This is where the value of the triggers within the move struct are passed off to the correct ShapeBaseImage instance. Note that in the stock example, move->trigger[0] will contain the value of $mvTriggerCount0, which in turn is how many times the left mouse button was pressed on the client during those 32 ms appropriate for this move struct. move->trigger[1] contains the $mvTriggerCount1 value.
setImageTriggerState(...) acts as a state input to the CrossbowImage we scripted in crossbow.cs. Depending on which state we are currently in, this trigger may or may not have an actual effect--if you look at the 7 states in the script, you will see a couple that have that transition defined as active, such as state 2 (Ready state):
This means that if the weapon is in state 2 (Ready), and it receives a trigger event (from our ::updateMove() setImageTriggerState() call), it will move to the scripted state indicated--in this case, the "Fire" state.
That "Fire" state in turn has a script defined, which will be executed when the state is achieved:
which, when the appropriate namespace is added, turns into:
In that script (remember I said to study it above?) is where our projectile is created!
Client
Finally, The client is responsible for handling the appropriate animations, sounds, and other cool things programmed into our weapon's state machine. Most of the various capabilities are pretty obvious, and you can see for example that during the Fire state, the weapon will do a lot of things, such as playing the weapons's "Fire" sequence (animation), telling the audio system to play the CrossbowFireSound, and after a 0.2 second timeout, transitioning to the "Reload" state.