Author : Filipe Medeiros
Page : << Previous 10 Next >>
can get the mouse pointer's X and Y coordinates using the variables `mouse_x' and `mouse_y' (which should be screen coordinates) and the button status is in `mouse_b', which is a bit field like this:
Bits: 2 1 0 . . X = left button flag . X . = right button flag X . . = middle button flag
The remaining bits are unused at present. The three button flag bits are set if the corresponding button is pressed, clear if not. If there is no middle button, its flag bit is always clear.
So, to read each button you bitwise-AND (&) `mouse_b' with 1, 2 or 4 like so:
if (mouse_b & 1)
printf ("Left ");
if (mouse_b & 2)
printf ("Right ");
if (mouse_b & 4)
printf ("Middle ");
printf ("\n");
Note though that the value of `mouse_b' can (and does) change `behind your back'; the following sort of test might give strange results:
if (mouse_b)
{
printf ("Buttons pressed: ");
if (mouse_b & 1)
printf ("Left ");
if (mouse_b & 2)
printf ("Right ");
if (mouse_b & 4)
printf ("Middle ");
printf ("\n");
}
Imagine what happens if initially one of the buttons is pressed, so that the `if' condition passes, but then the button is released; the string "Buttons pressed:" will be printed, but since now none of the buttons are pressed neither "Left" nor "Right" nor "Middle" will be printed.
If this sort of oddity could be a problem, you can take a copy of the `mouse_b' variable and work from that; this is a bit like polling the joystick, in a way.
my_mouse_b = mouse_b;
if (my_mouse_b)
{
printf ("Buttons pressed: ");
if (my_mouse_b & 1)
printf ("Left "); /* etc */
}
6.3.1.3 Displaying the mouse pointer
To show the pointer on the screen, you simply call the function `show_mouse', telling it which bitmap to draw onto, for example:
show_mouse (screen);
Whenever the mouse is moved, Allegro will update the mouse pointer (unless you've told it not to -- see the Allegro documentation). Because of this, you have to hide the mouse before wrting anything to the bitmap it is shown on, otherwise Bad Things happen to the display. To do this, call `show_mouse(NULL)'.
Note, though, that the functions which actually draw the mouse pointer require Allegro's timer routines to be installed (as do several other Allegro components) -- that's (partly) how they manage to do things in the background. See Chapter 9: Timers, and in particular Subsec 9.2.1: Initialising the timer system, for more information.
6.3.1.4 Controlling the mouse pointer
If you need to adjust the range of the mouse, you can call:
set_mouse_range (min_x, min_y, max_x, max_y);
where `min_x' and `min_y' are the minimum X and Y values, and `max_x' and `max_y' are the maximum values.
If for any reason you want to move the mouse pointer to a certain location on the screen, call the function `position_mouse', passing the new X and Y coordinates of the mouse.
6.3.2 Direct mouse control
This technique involves, at each game cycle, seeing how far the mouse has moved in each direction and treating this in the same way as analogue joystick data. Examples of games that do this are:
* All of id Software's recent games -- e.g. Wolfenstein 3D, and all of the Doom, Heretic and Quake games.
* Other first person perspective shoot-'em-ups. Well, id Software deserved to have a class to themselves since (until recently, at least) they have always excelled at creating gameplay and atmosphere. Examples here: the Descent series, Duke Nukem 3D, Dark Forces.
* Flight simulators -- e.g. Jetfighter II, the Wing Commander games, X-Wing, Tie Fighter, etc.
I don't think it's a coincidence that this list contains only first person perspective games (i.e. games where the display is from the player's point of view). That's not to say that this method can't work well for other games; I think it's more obvious when this type of game benefits from it.
When using this system, you initialise the mouse in the same way as for the point-and-click system (see Subsubsec 6.3.1.1: Initialising the mouse).
To make this sort of control system you ask the DOS mouse driver for a mickey update. A "mickey" is a very fine measure of mouse movement, and is given relative to last time you asked. Allegro (as of v3.0) provides a function to do just this:
void get_mouse_mickeys (int *mickeyx, int *mickeyy);
Pass this function two pointers to `int' variables, and it will fill the variables pointed to with the change in the mouse's X and Y positions since the last call to this function. For example, if `dx' and `dy' are `int' variables,
get_mouse_mickeys (&dx, &dy);
will put the mickey differences into `dx' and `dy'. Note that the units are not the same as the units of `mouse_x' and `mouse_y' -- the mickey is a much smaller unit.
The example for this section, `src/6.4/ii', uses this technique to move our wonderful circle around by tying the mouse movement to acceleration. As [ed: will be] discussed below, this is a common model for more realistic games, effectively linking mouse movement and force applied to the object. You ought to take a look at this example, just to see how it works. [ed: When it gets written...]
6.4 Generic input
Given all the above input types, we have written several programs allowing us to control things with each. However, how can we write a game which allows the user to use whichever device they want to?
We could write versions of the reaction code to interpret each type of information, and only use the ones corresponding to the user's choice of device, but it would be better to minimise the number of links between our input routine and the reaction routine. The example in `src/6.5' shows a way of doing this; I suggest you refer to it while reading this. Look in particular at `input.c' and `input.h'.
We need a common format for the data which is sent to the reaction routines. We need information about how far the input device is in each direction, and information about the status of any fire buttons. I created this struct to hold the information:
struct input_t { int dx, dy; int fire1, fire2; };
Let's define `dx' and `dy' to be from -128 to 128, as in the joystick routines, indicating which direction the device is pointing and how much. `fire1' and `fire2' will be non-zero when the fire button in question is pressed. We're limited to two, because most joysticks only have two; it's often sensible to aim for the lowest common denominator when writing code to work over a variety of systems or configurations.
The input routine will need to fill in the information required every time it is called. For joystick input, this is simple; just copy the values across, since the magnitudes match up. If the joystick is digital, though, we should check `joy_left', `joy_right', etc, using the same system as for the keyboard, which follows.
For the keyboard, we will need to work out where it is "pointing'". Given a `left_key' and a `right_key', we can work out the "rightness" as follows:
rightness = (key[left_key] ? -1 : 0) + (key[right_key] ? 1 : 0);
Think about what this means in each situation: If neither key is pressed, both brackets are zero and it returns 0. If both are pressed, the first bracket given -1 and the second gives 1; so it returns 0 again. If only left is pressed, the first bracket gives -1 and the second gives 0, so it returns -1, and if the right key is pressed it returns 0+1, which is 1. Now we can adapt this to fit our structure by saying:
dx = (key[left_key] ? -128 : 0) + (key[right_key] ? 128 : 0);
dy = (key[up_key] ? -128 : 0) + (key[down_key] ? 128 : 0);
fire1 = key[fire1_key]; fire2 = key[fire2_key];
Hence any keyboard input is always like full deflection of the joystick.
As for the mouse, we'll use the direct control method; if we wanted a point-and-click interface we would be pretty much dependent on the user using a mouse. You could use these input routines to simulate that system with any input device, but we won't go into that here.
To map
Page : << Previous 10 Next >>