Repeating a command while control is active (solution/technique)
by Stephen Zepp · in Torque Game Engine · 06/18/2004 (7:58 am) · 21 replies
This technique is suggested for the (pretty rare) situations where you want to be able to have a command repeated continuously while the mouse is "inside" a particular area of the GUI. It is not a "drop in and play" resource, but more of a description/example of a technique you can use for your own project.
If all you need to do is to change the state of buttons, graphics, or other "one time" state changes, then I would suggest you look at the general properties of other GuiCtls.
Overview:
When the mouse first enters a gui control, the event.onMouseEnter is triggered. Similarily, when the mouse leaves a control, .onMouseLeave is triggered. Using scripting, you can catch these events and perform actions in script.
Sometimes, you want a certain command to be executed repeatedly when the mouse is within a certain control. This technique gives you the basics of having this happen, using schedule() and cancel().
First, you should create your Gui object. I used GuiMouseEventCtl, with a default profile, since I didn't want the "hotspot" on the screen to be visible. I would suggest that you make sure the ctl doesn't overlap any other ctl's, simply for ease of deconfliction. You could use FirstResponder to handle any overlaps, but for the purpose of this technique we are keeping it basic.
Once you have the object, give it a name (we'll use CatchMouseLeft, which is an area of the screen that is on the left border of the window, 20 pixels wide, from top to bottom). Now you can write your scripts to catch and process the events.
In this example, we want to send a "user input: scroll orbit camera clockwise" command every 50 milliseconds for however long the mouse is within our control.
Note: We have already written the AdvancedCamera modifications to handle an "orbit cam" (which will be available as part of the AdvCamera resource in a few days), as well as the server scripts to handle user input for the orbit cam and pass to the code. These commands and files are not part of this technique, other than as an example for how to implement.
It is critical that you have the CatchMouseLeft::onMouseLeave function, otherwise your scheduled repeat events will never end!
EDIT: Does anyone know how to put tick marks and quotes in a ..code.. ../code.. segment so they show properly? :)
Further Edit: Intersting....as soon as I edited the message, it did it automatically!
If all you need to do is to change the state of buttons, graphics, or other "one time" state changes, then I would suggest you look at the general properties of other GuiCtls.
Overview:
When the mouse first enters a gui control, the event
Sometimes, you want a certain command to be executed repeatedly when the mouse is within a certain control. This technique gives you the basics of having this happen, using schedule() and cancel().
First, you should create your Gui object. I used GuiMouseEventCtl, with a default profile, since I didn't want the "hotspot" on the screen to be visible. I would suggest that you make sure the ctl doesn't overlap any other ctl's, simply for ease of deconfliction. You could use FirstResponder to handle any overlaps, but for the purpose of this technique we are keeping it basic.
Once you have the object, give it a name (we'll use CatchMouseLeft, which is an area of the screen that is on the left border of the window, 20 pixels wide, from top to bottom). Now you can write your scripts to catch and process the events.
In this example, we want to send a "user input: scroll orbit camera clockwise" command every 50 milliseconds for however long the mouse is within our control.
Note: We have already written the AdvancedCamera modifications to handle an "orbit cam" (which will be available as part of the AdvCamera resource in a few days), as well as the server scripts to handle user input for the orbit cam and pass to the code. These commands and files are not part of this technique, other than as an example for how to implement.
function CatchMouseLeft::onMouseEnter()
{
// first, do the command
commandToServer('MouseBounceLeft');
//next, schedule the next iteration, and save the EventId for later cancel
$Gui::CurrentBounceIterationEventId = schedule(50, 0, repeatBounceLeft);
}
function CatchMouseLeft::onMouseLeave()
{
// we're done being "in the box", cancel any pending events
cancel($Gui::CurrentBounceIterationEventId);
}
function repeatBounceLeft()
{
// perform action
commandToServer('MouseBounceLeft');
// reschedule
$Gui::CurrentBounceIterationEventId = schedule(50, 0, repeatBounceLeft);
}It is critical that you have the CatchMouseLeft::onMouseLeave function, otherwise your scheduled repeat events will never end!
EDIT: Does anyone know how to put tick marks and quotes in a ..code.. ../code.. segment so they show properly? :)
Further Edit: Intersting....as soon as I edited the message, it did it automatically!
#2
06/18/2004 (3:56 pm)
Very nice, looking forward to checking out the orbit cam.
#3
Besides that, the orbit cam rocks. With a few adjustments this will be a fantastic camera mode for e.g. RTS games or action adventures.
06/23/2004 (12:18 pm)
Orbit camera added to the resource coming up soon. I've forgotten about it, so the fault is all mine. Sorry.Besides that, the orbit cam rocks. With a few adjustments this will be a fantastic camera mode for e.g. RTS games or action adventures.
#4
06/26/2004 (11:01 pm)
I thought of this the other day, using the gui events setting up regions and what not, pretty much everything you said, but you beat me to the punch hehe, well done Stephen!
#5
06/28/2004 (1:40 pm)
Two thumbs up, Stephen.
#6
(e.g. the camera keeps rotating as I hold down a key, not keep a mouse over a GUI control)
10/31/2004 (12:52 pm)
Can someone give me some tips on how to take this theory and apply it to keyboard input?(e.g. the camera keeps rotating as I hold down a key, not keep a mouse over a GUI control)
#7
Eg:
moveMap.bind( keyboard, a, moveleft );
That just says call the function moveleft when the key 'a' is pressed. You should be able to figure out the rest from there :)
10/31/2004 (5:25 pm)
If you look in your scripts to see how the other keys are bound you can then just write a function for the camera rotating.Eg:
moveMap.bind( keyboard, a, moveleft );
That just says call the function moveleft when the key 'a' is pressed. You should be able to figure out the rest from there :)
#8
The examples provided elsewhere for orbit-cams all bind camera rotation to a key, and then talk about how you can't get a key to repeat in TGE, and to take a look at this example as inspiration to come up with a solution.
However, this solution seems pretty tied to mouse events in the GUI. What I'm unable to do is extend its metaphor into the realm of keyboard interaction.
So I get how to bind keys to events, but what I don't get is how to schedule key events to repeat, as the mouse event here does. Most especially, I don't think TGE has a onKeyUp event to correlate with the mouse leaving the action region.
So, I've got an orbit camera, I've bound rotating it to keys, but I need to make it so that when I hold the j key down, it keeps on rotating until I let go.
10/31/2004 (8:17 pm)
Let me rephrase my quandry:The examples provided elsewhere for orbit-cams all bind camera rotation to a key, and then talk about how you can't get a key to repeat in TGE, and to take a look at this example as inspiration to come up with a solution.
However, this solution seems pretty tied to mouse events in the GUI. What I'm unable to do is extend its metaphor into the realm of keyboard interaction.
So I get how to bind keys to events, but what I don't get is how to schedule key events to repeat, as the mouse event here does. Most especially, I don't think TGE has a onKeyUp event to correlate with the mouse leaving the action region.
So, I've got an orbit camera, I've bound rotating it to keys, but I need to make it so that when I hold the j key down, it keeps on rotating until I let go.
#9
10/31/2004 (8:52 pm)
Key callbacks get called when the key goes down and again when it goes up. What you do in the interim is your own business - schedule is one possibility.
#10
I've only been working with TGE for a day now, so I'm wondering if you can point me in a direction to better understand my options. I've been toying with schedule (as per this example) but it doesn't seem to do anything. I can't even get it into an infinite rescheduling, which I thought should have been dead easy.
If there is some docs or an example that uses this to do something similar, I'd be eternally grateful.
10/31/2004 (8:58 pm)
Thanks Ben.I've only been working with TGE for a day now, so I'm wondering if you can point me in a direction to better understand my options. I've been toying with schedule (as per this example) but it doesn't seem to do anything. I can't even get it into an infinite rescheduling, which I thought should have been dead easy.
If there is some docs or an example that uses this to do something similar, I'd be eternally grateful.
#11
There is example code in resources, in the example app, and on the forums... I'd give you some but I probably wouldn't bother to test it, so you can probably save time by finding some working schedule code... :)
10/31/2004 (10:53 pm)
Infinite scheduling means making the scheduled function call schedule again... an idiom, it's true, but thankfully not too tricky.There is example code in resources, in the example app, and on the forums... I'd give you some but I probably wouldn't bother to test it, so you can probably save time by finding some working schedule code... :)
#12
10/31/2004 (11:55 pm)
Well - the repeated scheduling is actually in the example code for this thread in the top.
#13
11/01/2004 (8:26 am)
Well then. :P
#14
At this stage I'm not understanding everything I'm doing, so its time for a stupid question to help me learn. Is $Gui::CurrentBounceIterationEventId an appropriate variable to be using inside the processing of keyboard events? It works, but what I'm doing seems fairly disassociated from the GUI.
For anyone looking to reply, assume I'm really really stupid. I'm not, but I kind of feel like it right now. ;-)
11/01/2004 (4:46 pm)
Thanks for the attention guys. Combined with this thread I've gotten it all figured out.At this stage I'm not understanding everything I'm doing, so its time for a stupid question to help me learn. Is $Gui::CurrentBounceIterationEventId an appropriate variable to be using inside the processing of keyboard events? It works, but what I'm doing seems fairly disassociated from the GUI.
For anyone looking to reply, assume I'm really really stupid. I'm not, but I kind of feel like it right now. ;-)
#15
11/02/2004 (11:47 am)
$Gui::CurrentBounceIterationEventId - where does that come from?
#16
11/02/2004 (11:48 am)
Nevermind, I'm stupid.
#17
This might make more sense to you....
Not sure why Stephen didn't fully utilize the object methods but if you changed the function to this, it would more resemble what you are probably used to seeing, and you won't be setting unneeded globals...
11/02/2004 (4:19 pm)
Edward...This might make more sense to you....
Not sure why Stephen didn't fully utilize the object methods but if you changed the function to this, it would more resemble what you are probably used to seeing, and you won't be setting unneeded globals...
function CatchMouseLeft::onMouseEnter(%this)
{
// FIRST, do this now or regret it later......
cancel(%this.iterationEventId);
// next, do the command
commandToServer('MouseBounceLeft');
//then, schedule the next iteration, and save the EventId for later cancel
%this.iterationEventId = %this.schedule(50, repeatBounceLeft);
}
function CatchMouseLeft::onMouseLeave(%this)
{
// Done, cancel
cancel(%this.iterationEventId);
}
function CatchMouseLeft::repeatBounceLeft(%this)
{
// perform action
commandToServer('MouseBounceLeft');
// reschedule
%this.iterationEventId = %this.schedule(50, repeatBounceLeft);
}
#18
@Gonzo: mostly because when I wrote this advanced camera mod, it was my first attempt in not only TGE but C++ code as well (I'm an old ANSI C guy). Your technique is much more elegant.
Interesting that this topic came up by the way--I'm not currently happy with the performance/look and feel of the current implementation of rotating the orbit cam--it can stutter based on your scheduler iteration time and net lag to the server. I'm looking at modifying it to make it more "on/off": the client sends a "start rotating in this direction" on a mouse enter, and a "stop rotating" on a mouse leave event, or something along those lines. This should give a "stutter free" rotation mode to the camera, but has an added issue of a start/stop rotating command possibly being missed across the UDP socket, and having the camera seem not quite as directly controlled.
Once I get the Terrain Manager implementation I'm working on more up to speed, and probably by then the RTS pack integrated, I'll work on this style of input to the adv cam (if no one else beats me to it).
11/04/2004 (3:31 am)
@Edward: Ironically, I wound up writing this type of interface because I couldn't get keys to repeat either ;)@Gonzo: mostly because when I wrote this advanced camera mod, it was my first attempt in not only TGE but C++ code as well (I'm an old ANSI C guy). Your technique is much more elegant.
Interesting that this topic came up by the way--I'm not currently happy with the performance/look and feel of the current implementation of rotating the orbit cam--it can stutter based on your scheduler iteration time and net lag to the server. I'm looking at modifying it to make it more "on/off": the client sends a "start rotating in this direction" on a mouse enter, and a "stop rotating" on a mouse leave event, or something along those lines. This should give a "stutter free" rotation mode to the camera, but has an added issue of a start/stop rotating command possibly being missed across the UDP socket, and having the camera seem not quite as directly controlled.
Once I get the Terrain Manager implementation I'm working on more up to speed, and probably by then the RTS pack integrated, I'll work on this style of input to the adv cam (if no one else beats me to it).
#19
this is how I implemented the event, I think it's what you are describing, or could be with a few changes.
11/04/2004 (3:41 am)
LOL, funny you should mention that....function PGLeftScroll::onMouseEnter(%this)
{
cancel(%this.CurrentMouseEvent);
turnLeft(0.075);
%this.CurrentMouseEvent = %this.schedule(0, repeat, 0);
}
function PGLeftScroll::onMouseLeave(%this)
{
cancel(%this.CurrentMouseEvent);
turnLeft(0);
}
function PGLeftScroll::repeat(%this, %speed)
{
if(%speed == 0)
{
%speed = 0.075;
}
else
{
%speed = 0;
}
turnLeft(%speed);
%this.CurrentMouseEvent = %this.schedule(200, 0, repeat, %speed);
} this is how I implemented the event, I think it's what you are describing, or could be with a few changes.
#20
Here's how I did the key repeats for the camera resource. It only uses one function, but seems to work.
Edit: ahh, [ code ] [ / code ] helps
01/21/2005 (7:17 pm)
@Edward:Here's how I did the key repeats for the camera resource. It only uses one function, but seems to work.
function AvatarCamPitchDown(%val)
{
if (%val) {
commandToServer('MouseBounceDown');
$CamPitchDownID = schedule(50,0,AvatarCamPitchDown,1);
} else {
cancel($CamPitchDownID);
}
}
and ...
moveMap.bind(keyboard, "h", AvatarCamPitchDown);Edit: ahh, [ code ] [ / code ] helps
Associate Kyle Carter