So far, we've sent keystrokes to the There window, but not timed keystrokes -- the difference being whether the exact times between key-down and key-up are significant. Now, let's send some timed keystrokes to the up, down, left and right arrow keys, causing our avatar to walk, turn, or run.
Be sure that you've followed the directions in Part 1 and invoked Win32::API, as well as having defined $key and $sleep. Now remembering hex keycodes is not nice, so put also some definitions near the top of your script
$arrowcodes{"L"} = hex("0x25");
$arrowcodes{"U"} = hex("0x26");
$arrowcodes{"R"} = hex("0x27");
$arrowcodes{"D"} = hex("0x28");
Here is the basic subroutine for pressing an arrow key for
a specified time, then releasing it and (optionally) doing
nothing for some additional time. All times are in milliseconds.
sub SendTimedArrow {
my ($code,$time,$sleepfor) = @_;
$key->Call($arrowcodes{$code}, 0x45, 0x01, 0);
$sleep->Call($time);
$key->Call($arrowcodes{$code}, 0x45, 0x03, 0);
$sleep->Call($sleepfor);
}
We're going to assume that you've already set the window focus
to the There window (see code example at the end of this part).To step forward some number of steps, call this subroutine
sub StepAvatar {
my $steps = shift;
&SendTimedArrow("U",int(475*$steps),400);
}
Of course this is only an approximation, because one step
does not always take exactly 475 milliseconds -- it depends on
the terrain, and may even depend a bit on the previous state
of your avatar. Play around with the constant if you are not
getting the results you want.A backwards step seems to take about 680 milliseconds, so we have
sub StepBackAvatar {
my $steps = shift;
&SendTimedArrow("D",int(680*$steps),400);
}
Turning left and right is even more of an approximation, since
turns seem to have long "tails" to them in time. I find that
615 milliseconds approximates a 90 degree turn to the left or
right.
sub TurnLeftAvatar {
&SendTimedArrow("L",615,400);
}
sub TurnRightAvatar {
&SendTimedArrow("R",615,400);
}
In the section on autopilots, you will learn how to make your
avatar turn by exactly some amount (or to some exact heading)
by sensing your heading and correcting within a feedback
control loop.Finally, running. Here we need a sequence of up-arrow key clicks, two 100 millisecond clicks to get into run mode, and then a long key-press for the number of steps. As before these are only approximations. (For some reason, I coded these with the elementary calls to Win32::API instead of using our SendTimedArrow() subroutine. I forget why!)
sub RunAvatar {
my $steps = shift;
$key->Call(0x26, 0x45, 0x01, 0);
$sleep->Call(100);
$key->Call(0x26, 0x45, 0x03, 0);
$sleep->Call(100);
$key->Call(0x26, 0x45, 0x01, 0);
$sleep->Call(100);
$key->Call(0x26, 0x45, 0x03, 0);
$sleep->Call(100);
$key->Call(0x26, 0x45, 0x01, 0);
$sleep->Call(312*$steps);
$key->Call(0x26, 0x45, 0x03, 0);
$sleep->Call(500);
}
If you think you might have cruise control on, then you'll want
to follow any call to RunAvatar (or any call to StepAvatar with
a large number of steps) by a quick press of the down-arrow:
&SendTimedArrow("D",100,100);
Here is a code fragment, using the above subroutines,
that steps your avatar forward 12 paces,
turns left, runs 50 paces, stops for 3 seconds, steps back 5 paces, and turns
right.
Win32::GuiTest::SetForegroundWindow(&GetThereWindow());
&StepAvatar(12);
&SendTimedArrow("D",100,100);
&TurnLeftAvatar();
&RunAvatar(50);
&SendTimedArrow("D",100,3000);
&StepBackAvatar(5);
&SendTimedArrow("D",100,100);
&TurnRightAvatar();
Next: Part 5