Thư viện tri thức trực tuyến
Kho tài liệu với 50,000+ tài liệu học thuật
© 2023 Siêu thị PDF - Kho tài liệu học thuật hàng đầu Việt Nam

C++ Programming for Games Module II phần 5 doc
Nội dung xem thử
Mô tả chi tiết
131
15.2 Shape Primitives
15.2.1 Drawing Lines
Line drawing is done with two functions. The first function moves the “virtual pen” to the starting point
of the line. The second function draws a line from the previously specified starting point, to a newly
specified second point:
// Following two functions draws a line from (startX, startY)
// to (endX, endY).
MoveToEx(hdc, startX, startY, 0);
LineTo(hdc, endX, endY);
Both functions take a handle to the device context, as all drawing must be done through the device
context. The second and third parameters for both functions are the x- and y-coordinates of a point,
which is relative to the client area rectangle. For MoveToEx, that point is the starting point of the line.
For LineTo, that point is the ending point of the line. The fourth parameter of MoveToEx returns a
POINT object of the last “start” point that was specified, via a pointer parameter. If this value is not
needed, you can specify null.
The program we will write to illustrate line drawing allows the user to define the line’s “start” point by
pressing the left mouse button. The user holds the left mouse button down and moves the mouse to a
new point. When the user has found the point where he/she wants the line’s “end” point to be, the user
raises the left mouse button up. Figure 15.3 shows the program after some lines were drawn.
Figure 15.3: The line drawing program. Users can draw lines by holding the left mouse button down
132
As the user moves the mouse around looking for the “end” point, he/she will expect to see the new line
being drawn in real-time. That is, a line from the “start” point to the current mouse position should
constantly be drawn and updated interactively. In this way, the user can see exactly how the line will
look before raising the left mouse button to make the line permanent. This functionality requires some
special code. Let us get started.
First, we have the following global variables (and also a structure definition):
struct Line
{
POINT p0;
POINT p1;
};
vector<Line> gLines;
Line gLine;
bool gMouseDown = false;
A Line is simply defined by two points, p0, and p1, where p0 is the “start” point and p1 is the “end”
point.
We recall that Windows does not save our drawn data if a part of the client area gets obscured.
Therefore, we need to save all the data ourselves so that we can redraw it all when a WM_PAINT message
occurs. To facilitate this, we maintain a global vector of Lines, called gLines, which will store the
lines we create. The global variable gLine is our temporary line; that is, it is the line we will draw as
the user moves the mouse around when deciding where the “end” point of the line should be. We do not
actually add a line to gLines until the user has lifted the left mouse button. Finally, gMouseDown is a
Boolean variable that denotes whether or not the left mouse button is currently down or not.
The first message we need to handle is the WM_LBUTTONDOWN message, which is where the line’s
“starting” point is defined.
case WM_LBUTTONDOWN:
// Capture the mouse (we still get mouse input
// even after the mouse cursor moves off the client area.
SetCapture(hWnd);
gMouseDown = true;
// Point that was clicked is stored in the lParam.
gLine.p0.x = LOWORD(lParam);
gLine.p0.y = HIWORD(lParam);
return 0;
Note that we set the “start” point in our temporary line. We do not actually add the line to our global
line container gLines until the user lifts the left mouse button.
133
A new API function in this message handler is the SetCapture function. This function “captures” the
mouse for the specified window. Capturing means that the window will continue to receive mouse
messages even if the mouse moves off the window’s client area. As long as the user has the left mouse
button down, we would like to have the mouse captured—we free the mouse when the user lifts the left
mouse button. Finally, if a WM_LBUTTONDOWN message occurs, we know the mouse is now down, so we
set our flag gMouseDown to true.
The next message we handle is the WM_MOUSEMOVE message. This message is sent whenever the mouse
moves.
case WM_MOUSEMOVE:
if( gMouseDown )
{
// Current mouse position is stored in the lParam.
gLine.p1.x = LOWORD(lParam);
gLine.p1.y = HIWORD(lParam);
InvalidateRect(hWnd, 0, true);
}
return 0;
Notice that we only care about this message if the left mouse button is down (if( gMouseDown )). If it
is not, we do not care about the WM_MOUSEMOVE message and do not execute any code.
So, assuming the left mouse button is down, as the mouse moves we obtain the current mouse position
(given in the lParam for the WM_MOUSEMOVE message) and set it as the “end” point for the temporary
line. We then invalidate the window’s client rectangle so that it is forced to repaint itself. In this way,
the new temporary line will be redrawn interactively as the mouse moves. Also note that here we
invalidate the rectangle with true specified for the third parameter—this will cause the background to
be erased, which is necessary since we need to erase any previously drawn temporary lines. That is,
every time the mouse moves we will draw a temporary line, but we do not want to accumulate these
lines; we just want to draw the latest temporary line. Therefore, we must erase any old lines.
The third message we handle is the WM_LBUTTONUP message. This message is generated when the left
mouse button is lifted up.
case WM_LBUTTONUP:
// Release the captured mouse when the left mouse button
// is lifted.
ReleaseCapture();
gMouseDown = false;
// Current mouse position is stored in the lParam.
gLine.p1.x = LOWORD(lParam);
gLine.p1.y = HIWORD(lParam);
gLines.push_back( gLine );