Use C ++ and DirectX to develop gui (1)

Source: Internet
Author: User

It seems that I am doing a waste of time to invent the wheel-windows already has a very complex and functional GUI. unfortunately, Windows guis apply to office software, which is usually not suitable for game software. games generally require more accurate control than Windows (for example, it is easier to use your own GUI to implement a part of transparent windows created with Alpha mixture, while Windows GUI is almost impossible ).

This article describes how to use C ++ and DirectX to create your own GUI. this article consists of several parts, each of which involves a specific GUI programming topic. this does not need to be read in order, so you can start from what you need.

Here, I assume that the reader has the following background knowledge: the event-driven programming principle; Proficiency in PDL and C ++. I use C ++ to build my GUI system, because I am a card member of the C ++ fans club, and C ++ OOP is very suitable for Windows Programming and control methods.

Let's start with defining the scope of work. we should realize that we don't want to design Windows 95, we just want to develop a simple GUI for a game. therefore, we do not need to implement every simple control and GUI structure. we only need a few parts: A mouse pointer, a common window, and some dialog box control in the window. we also need a resource editor-a dialog box that allows us to design with Drag Control in the graphic environment.

Starting from the basics: rendering Loop

I will start by defining a function that calculates and draws a box. We will call this function in the rendergui () function. The rendergui uses PDL to describe it as follows:

Void capplication: rendergui (void)
{
// Get position and button status of mouse cursor
// Calculate mouse cursor implements effects on Windows/send messages
// Render all windows
// Render mouse
// Flip to screen
}

We get a new position and the state of the mouse cursor, calculate any changes caused by the new data, render all windows, and draw the mouse pointer on the screen.

Mouse

Class cmouse {
Public:
Cmouse (); // boring
~ Cmouse (); // boring

Int Init (lpdirectinput di); // We recommend l talk about this later
Int refresh (void); // We recommend l talk about this later

Int getbutton (INT index)
{
If (index <0 | index> nummousebuttons) Return (0 );
Return (m_button [Index]);
}

Void clear (void); // sets all vars to zero

// Makes sure P is a valid on-screen point
Void constrainpostoscreensize (cpoint & P );
Cpoint getabsposition (void) {return (m_absposition );}
Cpoint getrelposition (void) {return (m_relposition );}
Enum {nummousebuttons = 3}; // three button mouse

PRIVATE:
Lpdirectinputdevice m_mousedev;
Char m_button [nummousebuttons]; // state of buttons
Cpoint m_absposition; // actual screen position
Cpoint m_relposition; // relative position

};

Absolute and relative positions, directinput

Why should I use directinput? This is actually a matter of taste. there are two ways to get Windows mouse information, from directinput or through a Win32 function called getcursorpos. the main difference is that directinput provides relative coordinates, that is, the current position relative to the last position; while getcursorpos () provides the absolute coordinates of the screen coordinate system. absolute coordinates are useful for GUI, while relative coordinates are suitable for mouse without a cursor, for example, in FPS games ). however, you can calculate relative coordinates from absolute coordinates, and vice versa.

For many reasons, I chose directinput, which is beyond the scope of this article. For you, getcursorpos () may be a better choice? In this case, the mouse class may be very large. directinput is more skillful (and more interesting), so this article will focus on directinput.

Initialize directinput

Before we detail cmouse, let's take a look at how to initialize directinput. note that these codes do not belong to our cmouse: Init () function; directinput pointers are used throughout the game, not just in the mouse class, therefore, the initialization of directinput should be completed at the same time in the main initialization function and the initialization of DirectDraw and directsound. the directinput pointer is different from the directinput device pointer. The directinput pointer is used to obtain the directinput device pointer. note that the following code initializes the pointer of the main directinput interface.

Lpdirectinput di = NULL;

HR = directinputcreate (hinst, directinput_version, & Di, null );

If (failed (HR )){
// Error
Handle_error ();
}

// Now that we have e got a directinput interface, let users begin
// Fleshing out our cmouse by implementing cmouse: Init ().

Bool cmouse: Init (lpdirectinput di)
{
// Obtain an interface to the system mouse device.
HR = di-> createdevice (guid_sysmouse, (lpdirectinputdevice *) & m_mousedev, null );

If (failed (HR) {/* handle errors! */}
// Set the data format to "Mouse format ".
HR = m_mousedev-> setdataformat (& c_dfdimouse );
If (failed (HR) {/* handle errors! */}
// Set the cooperativity level
HR = m_mousedev-> setcooperativelevel (hwnd, discl_nonexclusive | discl_foreground );
If (failed (HR) {/* handle errors! */}
}

This Code has made three important tasks. first, get a valid directinput mouse interface and pay it to di_mouse. second, set the data format and the degree of collaboration between the device, so that Windows only knows that we want to query the mouse device, rather than monopolizing the device. (exclusive means that only our applications can use the mouse. By specifying discl_nonexclusive, we will notify windows that we will share the mouse with other programs)

Use directinput to check the mouse status

Now let's take a look at cmouse: refresh (). This function updates the internal key status and location of cmouse. The following code is used:

Void cmouse: refresh (void)
{
Char done = 0;
Int Q;
Hresult hr;
Cpoint P;
Dimousestate dims;

// Clear our struct? Eventually, directinput will fill this in
Memset (& dims, 0, sizeof (dimousestate ));

If (! M_mousedev) return; // we don't have a pointer! Bail!

While (! Done)
{
// Query directinput for newest mouse data
HR = m_mousedev-> getdevicestate (sizeof (dimousestate), & dims );
If (failed (HR ))
{
If (hR = dierr_inputlost | hR = dierr_notacquired)
{
// Device lost... reacquire
HR = m_mousedev-> acquire ();
If (failed (HR ))
{
// Error handling goes here
Clear ();
Done = 1;
}
}
Else
{
// It does some other error? Handle it
Clear ();
Done = 1;
}
}
Else // read mouse successfully!
{
Done = 1;
}
} // End while loop? We recommend e read di correctly

// Squirrel away newest rel position data
M_relposition.x = dims. lx;
M_relposition.y = dims. ly;
M_relposition.z = dims. LZ;

// Now calc ABS position from new relative data
M_absposition.z + = dims. LZ;
M_absposition.x + = dims. lx;
M_absposition.y + = dims. ly;

// Keep the mouse pointer on-screen...
Constrainpostoscreensize (m_absposition );

// Get button data
For (q = 0; q <nummousebuttons; q ++)
{
M_button [Q] = (dims. rgbbuttons [Q] & 0x80 ));
}

}

This code has done a lot of work. first, get the new absolute mouse position through directinput (use the while loop to automatically re-detect ). next, pay the absolute position data to m_absposition and obtain a new relative position. the constrainpostoscreensize () function ensures that the mouse is located within the screen range. finally, all keys are updated cyclically.

Draw mouse

There are two main principles for drawing the mouse light. if you know that each frame of the screen will be refreshed with new pixel data, you can simply place the BLT cursor to the new absolute position and draw it accordingly. however, a better way is to get a copy of the pixel data under the mouse cursor before you BLT, and then, when the mouse moves, the old BLT is erased by the pixel data stored by BLT. I prefer the latter method.

This article will not discuss the specific BLT surface and others. You should know how to implement it.

Thread and trail

If you don't mind multithreading, there is a better way to solve the mouse problem. The method here is a single thread, and the mouse will be queried for each frame.

This method is applicable to high update rate, rather than low update rate. the mouse cursor will appear very slow. the best way is to create an independent mouse-rendering thread that constantly detects the mousemove message and refreshes and BLT the mouse cursor when the mouse moves. the advantage of Multithreading is that no matter whether your game refresh rate

A single mouse thread will make the game quickly at any frame frequency.

Creating a mouse track is obviously easy for you. save the last few cursor positions in an array. when the mouse moves, discard the oldest coordinates, move all other coordinate values down one column, and store the new coordinates in the top column. then, if you want additional effects. use alpha blending to render old coordinates with greater transparency than new coordinates.

Looking forward...

Now we have finished the mouse and cursor work. The next section will show you how to create a basic form and how to move the form.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.