Preliminary game programming I-Preliminary Windows Programming

Source: Internet
Author: User

Note: It has been two months since I encountered some emotional problems recently. People who think they are rational are more emotional than ordinary people. It's time to come out slowly. Only the knowledge is real, and it will always belong to you after learning. You must do something else. I have a limited level of English. Do not mind if you think there is anything wrong with the text. The copyright can be reproduced at will, but please indicate the original link of the article. The copyright belongs to the original author.

Address: http://www.gamedev.net/reference/programming/features/gpgenesis1/default.asp

Introduction:
This section describes the basic knowledge of Windows programming. Finally, you can use what you have learned to complete simple windows programming. This part is the foundation of C language. In my code, I almost never use the extension feature of C ++. However, Windows itself is a forward object, so it involves a little bit of class knowledge, but it will not become an obstacle for you to learn. If you are not familiar with it, you don't have to worry about it. You don't need to use anything that is too complicated, so you will learn it soon as you look at it. All the sample code is compiled and tested in Microsoft Visual C ++ 6.0. If you do not have a Win32 C/C ++ compiler, vc6 is a good choice. Now let's start our journey!

Installation:
We need the windows. h and Windows X. h header files, which include most of the Windows methods. Make sure they are included in your program. In addition, you need to use standard C header files, such as stdio. h and conio. h. In addition, there is such a line of code in the first line of many Windows programs:
# Define win32_lean_and_mean
It has a special significance. It removes some MFC-related resources from the Windows header file and increases the compilation speed and reduces the time. You may be reluctant to use MFC in your game programming, so this is a good solution in most cases. Before that, you may not have seen this kind of statement-a # define command followed by a macro-which is used for Conditional compilation. Let's look at an example:
# Ifdef debug_mode
Printf ("debug mode is active! ");
# Endif
If this sample code is included in the program and # define debug_mode has been encountered before, the printf () statement will be compiled, but not compiled. This method is very useful in your code. It will switch your code and help you eliminate possible logical errors. The win32_lean_and_mean macro is used to disable components rarely used in Windows header files. Do you understand? Good. Let's go to the actual code...

Winmain () method:
Similar to the C Programming in DOS, the main () method is used as the execution entry, while the windows program uses the winmain () method as the execution entry. The simplest wimmain () method is as follows:
Int winapi winmain (hinstance, hinstance hprevinstance,
Lpstr lpcmdline, int ncmdshow)
{
Return (0 );
}
In addition to returning a value, this method does nothing, and it also brings many new features. First, what is the use of the Declaration as a winapi? This is a description of the call conventions. It is related to the way the parameter is passed into the method, and this method acts as a stack cleaning, it is also related to something that you simply cannot see. When a method uses the winapi call convention, parameters are passed from left to right, which is opposite to the default call Convention from right to left. Unless you use assembly instruction programming, you do not need to know the details of the call conventions. Only winmain () must require the winapi call conventions. Next, let's take a look at the four parameters of this method:

Hinstance: This is the instance handle of your program. Basically, they are similar to pointers to track all programs running at any time. Many windows methods require the instance handle as the parameter, So it knows which application sends the call request.

Hinstance hprevinstance: you do not need to worry about this parameter, so it is no longer used. In earlier versions of Windows, this point is to enable your current program. The reason for retention is backward compatibility. In the future windows programming, you will see many similar situations.

Lpstr lpcmdline: it is a pointer pointing to a security string that contains the command line parameters when the program is called. Note that there is no parameter to describe the number of command line parameters here, so you need to decide on your own.

Int ncmdshow: This integer indicates how the main window appears. If you do not want to specify it, you can do nothing. Its values start with SW. For example, sw_shownormal indicates the silent mode, and sw_maximize and sw_minimize indicate the maximum and minimum Windows, respectively.

These are the parameters about winmain. Often, hinstance has only one important role. Before we continue to explain how to display a window, Microsoft's variable name remains to be explained.

Hungary Name:
Microsoft uses a set of standard naming rules for variables, methods, constants, and classes, which are the Hungarian naming rules. You have seen this naming method in winmain. This naming method adds a prefix that represents the data type before the variable name. Here are some prefix usage instructions:
B bool (INT)
By byte or uchar (unsigned char)
C char
CX, Cy short (usually lengths; C stands for "count ")
Dw dword (unsigned long)
FN function pointer
H handle (like a pointer, used for Windows objects)
I int
L long (long INT)
LP long pointer (32-bit)
MSG message (we'll cover this later)
N number (short or INT)
S string
SZ, STR null-terminated ("asciiz") String
W word or uint (unsigned INT)
X, Y short (usually coordinates)

In addition, if the variable name is more than one word, each word must start with an uppercase letter, and no underline is required between the word and the word. For example, if a pointer pointing to the memory area and its name is related to player data, it may be expressed as lpplayerdata in different cases. This standardized naming rule is helpful for the code. Take the preceding winmain () method as an example. If you do not need to check its help documentation, its naming method illustrates the meaning of the parameter. hinstance and hprevinstance are handles, the lpcmdline is a 32-bit long pointer, while the ncmdshow is an integer.

Using the Hungary naming method to name a function is the same as the variable naming method, but the prefix is removed. That is to say, the first letter of the method name is in upper case, and all the words in the method name are in upper case. For example: showcharacterstats ();

The naming rule for constants is that all letters are capitalized and words or prefixes are distinguished by underscores. For example, win32_lean_and_mean. In Windows, you will see that there is often a method name abbreviation before a constant, and this method is to use this constant method. For example, the previously mentioned constants sw_shownormal, sw_maximize, and sw_minimize all have a SW _ prefix, which indicates that these constants will be used by showwindow () in front of parameters.

Finally, the class name is the same as the method name except the first uppercase C letter. For example, the speed class used in the speed competition game can be named cvehicle.

In your program, you may not use this naming method, but I should be familiar with it, because all Microsoft products are in this set of rules. If you choose to use it, it will be very convenient. I will follow this rule. No matter what you do, we will continue learning...

Message
When you write a program on DOS, you do not need to worry about other program running conditions, because DOS is a single task operating system. But in Windows, you must consider it. For various reasons, the message mechanism is used in Windows to communicate with the application. It can tell the program what to do. Messages have many functions. Through the message, the application knows when the window is changed, when it is moved, and when it is closed. They signal when the program is closed. They notify the program when the window needs to be re-painted. It can track mouse movement and Button clicking. In this way, the number of sub-scripts is exceeded. In any case, your window program must be able to process these messages.

The special method for processing these messages is called the callback function. You do not need to call a callback function in your code, but an event that triggers it to be called. You can call callback to create a callback function, just like the winapi call convention in winmain. I will end this topic as soon as possible, because you should be able to understand how to create a window before you can process messages.

Window class:

Here you will learn something about C ++, because you must create a window class before creating a window. This class includes all information about the window, such as the icons used and associated menus. All the window programs you create require a window class that meets your requirements. All you have to do is fill in a wndclassex struct. "Ex" indicates "extended", indicating that the struct of a previous version is wndclass. We will use the extended version. The following is his true face:
Typedef struct _ wndclassex {
Uint cbsize;
Uint style;
Wndproc lpfnwndproc;
Int cbclsextra;
Int cbwndextra;
Handle hinstance;
Hicon;
Hcursor;
Hbrush hbrbackground;
Lpctstr lpszmenuname;
Lpctstr lpszclassname;
Hicon hiconsm;
} Wndclassex;
 
This struct has many members, and the created window class must be completely filled. In fact, it is not difficult. Next we will briefly describe its members.

Uint cbsize: indicates the size of the struct in bytes. You will often see such struct members, especially in dx. The reason for its existence is that, if this struct is passed to a method as a parameter, you do not need to calculate it. You just need to check it to know the size of the struct. Its value is always sizeof (wndclassex ).

Uint style: This is the window style, which is a constant prefixed by CS. In addition, you can use the | Operator to connect several constants. Generally, you only need four of them. To reduce the length of the article, I will show only the four. You can view the rest through msdn. Remember to get visual C ++.
Cs_hredraw specifies that the window shocould be redrawn if it is horizontally resized.
Cs_vredraw specifies that the window shocould be redrawn if it is vertically resized.
Cs_owndc allows each window to have a unique device context or DC (not covered in this article ).
Cs_dblclks discerns between single-and double-clicks while the mouse is in this window.

Wndproc lpfnwndproc: A pointer pointing to a callback function that processes window messages. If you have never used a function pointer, it actually points to the function address, just like a function name, but it has no parentheses behind it.

Int cbclsextra: it is used to receive other class information. Generally, it is not used. When you develop a game, you won't find it used, so set it to 0.

Int cbwndextra: similar to cbclsextra, But it stores extended window information. You only need to set it to 0 when using it.

Handle hinstance: This is the application instance handle that uses this window class. It is passed to a parameter in winmain () and they must be equal.

Hcursor: When the mouse is in the window, it processes the cursor. It often uses the return value of loadcursor () to assign values. Of course, you can load your own defined cursor. It will take you to learn how to load it. Alternatively, you can use the standard window cursor in the format of loadcursor (null, idc_arrow ).

Hbrush hbrbackground: when you receive a refreshed (or re-painted) message in the window, the window will be re-painted with solid color or paint. This parameter defines a painter. You can use getstockobject () to load several solid color paint brushes, including black_brush, white_brush, and cary_brush. Now you can use getstockobject (black_brush) in this way ). Sorry, I am so brief about these methods. I am trying to Reduce the length. I will talk about related knowledge points in later articles. I promise!

Lpctstr lpszmenuname: If you want to create a pop-up menu, this parameter is the name of the menu to be loaded and associated with the window. Because you do not know how to create a menu, if you set it to null, there is no menu.

Lpcstr lpszclassname: name of the class to be created. You can name the name you like. It is best to use a descriptive name. For example, "game_class.

Hicon hiconsm: there is a small icon in the title bar and start menu bar of the window, and this small icon is passed in by this parameter. It is the same as setting hicon. You can use loadicon. Now, we will use loadicon (null, idi_winlogo) to load a standard window logo icon.

This is the description of all parameters. Now you are familiar with all the members of the wndclassex struct. You can fill it in and prepare the window for creation. The following is a simple example:
Wndclassex sampleclass; // declare Structure Variable

Sampleclass. cbsize = sizeof (wndclassex); // always use this!
Sampleclass. Style = cs_dblclks | cs_owndc |
Cs_hredraw | cs_vredraw; // Standard settings
Sampleclass. lpfnwndproc = msghandler; // we need to write this!
Sampleclass. cbclsextra = 0; // Extra Class info, not used
Sampleclass. cbwndextra = 0; // extra window info, not used
Sampleclass. hinstance = hinstance; // parameter passed to winmain ()
Sampleclass. hicon = loadicon (null, idi_winlogo); // Windows logo
Sampleclass. hcursor = loadcursor (null, idc_arrow); // standard cursor
Sampleclass. hbrbackground = (hbrush) getstockobject (black_brush); // a simple black brush
Sampleclass. lpszmenuname = NULL; // no menu
Sampleclass. lpszclassname = "sample class" // Class Name
Sampleclass. hiconsm = loadicon (null, idi_winlogo); // Windows logo again

You have set all the members. But here is something worth mentioning. Note that there is an hbrush forced type conversion when getstockobject () is returned. This is because getstockobject () can be used to load many objects, instead of painting them separately. Its return value is a variable of the hgdiobj type, which is a more general type. In earlier versions of Visual C ++, you do not need to force conversion, but Vc ++ 6.0 is more rigorous in this regard. If you do not force conversion, you will get a compilation error.

The last thing is that you need to register this new class with windows and then use this class to create a new window. You can complete registration by simply calling registerclassex. It has only one parameter: the address of the struct. Then, you can register it as follows:
Registerclassex (& sampleclass );

Now, Windows can understand the new class you have created. You can use it to create a window. Now the time is ripe, isn't it?

Creation window:

First, there is good news. When creating a window, you only need to call createmediawex (). There is also a bad message. This method has many parameters. Now you may get bored with it, but it's not that bad. The following is a method prototype:
Hwnd createmediawex (
DWORD dwexstyle, // extended window style
Maid, // pointer to registered Class Name
Lptstr lpwindowname, // pointer to window name
DWORD dwstyle, // window style
Int X, // horizontal position of window
Int y, // vertical position of window
Int nwidth, // window width
Int nheight, // window height
Hwnd hwndparent, // handle to parent or owner window
Hmenu, // handle to menu, or child-window identifier
Hinstance, // handle to application instance
Lpvoid lpparam // pointer to window-creation data
);

The first is about the returned value. Now, you may be familiar with the Data Types of windows. If you don't have them, you don't have to worry about them. You will use them immediately, but it is earlier than you thought. The return value is hwnd, which is the window handle. You want to save the returned values of createmediawex (), because it is used in many Windows functions. Now let's process the parameter list. Many parameters are self-evident.

Dworddwexstyle: Extended window style, which is rarely used. Therefore, in many cases, it is set to null. If you are interested, there are a lot of constants in the help document for you to use here, they all start with ws_ex.

Lpctstr lpclassname: Do you still remember to name the class? The name is needed here.

Lptstr lpwindowname: The content displayed in the title bar of the window.

DWORD dwstyle: specifies the type of the window to be created. There are many constants for you to use here. They all start with WS _ and you can use them together with the | Operator. The following is just a list of common features of the feature:
Ws_popup a window that has no controls built into it.
Ws_overlapped a window with simply a title bar and a border.
Ws_overlappedwindow a window with a title bar including all standard controls.
Ws_visible specifies that the window is initially visible.

Ws_overlappedwindow is often used together with other constants to create a standard window. Basically, you can follow this method to create. If you want to maximize and minimize the size of a window, you can change the size of the window... and use ws_overlappedwindow. If you want a window with a title bar and a fixed size, use ws_overlapped. if you want a window with no control buttons on it, use ws_popup. for example, a black area window appears. You can use it to create a full-screen game. Of course, you also need ws_visible, unless for some reason you do not want others to see your window, or you want to do other things before displaying the window.

Int X, Y: It indicates that the window appears at the Coordinate Position in the upper left corner of the screen.

Int nwidth, nheight: As you may have guessed, It is the length and width of the window, in pixels.

Hwnf hwndparent: It processes the parent window of the window you created. In most cases, it is used by controls, such as checkbox and Pushbutton. If it is the main window, set it to null, indicating the Windows desktop window.

Hmenu: It processes the menus associated with the window. If you load a resource menu -- after you learn how to load it -- you can use loadmenu (). If there is no menu Association, set it to null.

Hinstance: This is an application instance. Use the parameter passed in winmain () to pass in.

Lpvoid lpparam: this is something you don't like to use. Especially in game development, it is used only for a few windows. It is used to create something similar to multi-document interfaces. We set it to null.

Finally, we have everything we need to create a window. The following example shows how to do this:
Hwnd;
If (! (Hwnd = createappswex (null, // extended style, not needed
"Sample class", // Class Identifier
"Sample window", // window title
Ws_popup | ws_visible, // Parameters
0, 0,320,240, // initial position, size
Null, // handle to parent (the desktop)
Null, // handle to menu (none)
Hinstance, // application instance handle
Null) // who needs it?
Return (0 );

This window can be used to write games because it is a popup window. Note that I put the createdomainwex () call in IF. This is because if createwindowsex () fails to be called, it returns NULL. This is worth doing. For example, if windows fail to be created due to various reasons, winmain () will return and the program will end.

Now you have enough knowledge about Windows program development to create a functional window. Remember when we created "sample class", did we provide a pointer to the message processing method? Before Windows allows us to create other things, we need to do this.

Message Processing:
I have covered the application of messages in windows. Now let's repeat its usage. The prototype of the message processing method is similar to the following:
Lresult callback msghandler (
Hwnd, // window handle
Uint MSG, // The message identifier
Wparam, // message Parameters
Lparam // more message Parameters
};

The Return Value of the lresult type is specially designed for message processing functions, just like what we want to write. We have previously discussed the callback call convention. The parameters of this function are very simple:

Uint MSG: defines a message. Its value is a constant starting with WM _ (Windows message. The number of messages that can be sent is very large, but some important messages are listed here:
Wm_activate a new window is refreshing the focus.
Wm_close a window is being closed.
Wm_command a menu option has been selected.
Wm_create a window has been created.
Wm_lbuttondblclk left mouse button has been double-clicked.
Wm_lbuttondown left mouse button has been pressed.
Wm_mousemove the mouse has been moved.
Wm_move a window has been moved.
Wm_paint part of a window needs to be repainted.
Wm_rbuttondblclk right mouse button has been double-clicked.
Wm_rbuttondown right mouse button has been pressed.
Wm_size a window has been resized.
Wm_user use this for whatever you want.

Wparam, lparam: precise utilization of these parameters depends on what messages are sent, but they are used to describe the meaning of messages more accurately.

If you want to write code to process all the messages received in your window, this is a very stupid idea. I know that I used to think like this. Fortunately, Windows provides some default message processing methods. If you do not have any special need to process a message, you may call defwindowproc (). as long as you have this kind of thinking, there is a very simple one here, but the complete function of message processing functions for your reference:

Lresult callback msghandler (hwnd, uint MSG, wparam, lparam)
{
Return (defwindowproc (hwnd, MSG, wparam, lparam ));
}

Is it easy? Generally, you want to process your own messages. In this case, you can write your own code to process the message, and then return 0 to tell the program that you have processed the message. Here is an example of message processing. When a window is created, the initialization function is called, and other messages are processed by default.

Lresult callback msghandler (hwnd, uint MSG, wparam, lparam)
{
If (MSG = wm_create)
{
Initialize_game ();
Return (0 );
}

Return (defwindowproc (hwnd, MSG, wparam, lparam ));
}

In your message processing function, you can use a switch statement to allocate the desired message, and call the default processing function to process other messages. In order to give a smoother explanation in the future, we will provide you with several important knowledge points. It is how to ensure that your message processing function is executed.

Read Message Queue:

When you start the main loop of your program, you need to check whether there is a message waiting in the message queue. This message queue stores all pending messages. In order for you to process functions correctly, you need to do something. You need peekmessage (), which is prototype:
Bool peekmessage (
Lpmsg, // pointer to structure for message
Hwnd, // handle to window
Uint wmsgfiltermin, // first message
Uint wmsgfiltermax, // last message
Uint wremovemsg // removal flags
);

Its return value type is bool, which is actually int type, but it only has two values: true and false. If there is a waiting message in the message queue, it returns true. Otherwise, it returns false. The parameter description is clearer:

Lpmsg: pointer to the MSG type variable. If a message is waiting, the variable is filled with the message.

Hwnd: The Window handle of the Message Queue you want to detect.

Uint wmsgfiltermin, wmsgfiltermax: indicates that the first or last message in the queue is detected. Generally, you are interested in the first message, so you set both parameters to 0.

Uint wremovemsg: Generally there are two options: pm_remove or pm_noremove. The previous one indicates that after reading a message, the message will be removed from the message queue, and the last one indicates that the message will be retained in the queue after being read. Normally, if a message is waiting, you need to process it immediately, that is, you apply pm_remove.

When a message is waiting, you need to do something so that your message processing function can hit the message. You only need two simple calls: one is translatemessage () and the other is dispatchmessage (). Their function prototype is:

Bool translatemessage (const MSG * lpmsg );
Long dispatchmessage (const MSG * lpmsg );

As you may have guessed, the first method is used to remove a message. The second method calls your message processing function and sends the appropriate information in the MSG struct. You only need to understand this. What is executed in your main loop is: if a message is waiting, you will call the two methods above, and then your message processing function will complete the rest. Here is a sample code:
If (peekmessage (& MSG, null, 0, 0, pm_remove ))
{
Translatemessage (& MSG );
Dispatchmessage (& MSG );
}

No problem! Now you can create, register the window class, and create a valid message processing. It's not that hard to imagine, isn't it? Before I end this article, I will also mention some things. Back to the Message topic, it takes some time to manually send the message.

Send message

You can send messages in two ways. You can call postmessage () or sendmessage (). Their prototype is simple:
Bool postmessage (
Hwnd, // handle of destination window
Uint MSG, // message to post
Wparam, // first Message Parameter
Lparam // second Message Parameter
);

Lresult sendmessage (
Hwnd, // handle of destination window
Uint MSG, // message to post
Wparam, // first Message Parameter
Lparam // second Message Parameter
);

Their parameters are the same as those of the msghandler () method we wrote, so I don't want to explain them here. You only need to understand the differences between them, so I will explain them one by one.

Postmessage () is used to add a message to the message queue and let your program process it. If a non-zero value (true) is returned, otherwise, zero (false) is returned. It simply adds a message to the queue and returns immediately. In most cases, calling postmessage () can meet your needs.

Sendmessage () is a bit difficult. Note that the returned value is lresult, which is used in message processing functions. This is because it does not add a message to the queue. It transmits the message and calls message processing immediately. It is returned only after the message is processed. Sendmessage () is more suitable than postmessage () If a task has a high priority or requires rapid response. If you want to do something immediately, use it.

Now, you know, the topic of the message is the last content I mentioned. It is also the main difference between Windows Programming and DOS programming.

Process programming:
In the DOS era, you do not need to process these messages. You do not need to pay attention when multiple programs run at the same time. However, in windows, these messages are very important. Therefore, your programming is totally different from that in DOS. The following is a pseudo-code example:
// Main game loop
Do
{
// Handle messages here

//...

// Update screen if necessary
If (new_screen)
{
Fadeout ();
Loadnewmap ();
Fadein ();
}

// Perform game logic
Waitforinput ();
Updatecharacters ();
Rendermap ();

} While (game_active );

Imagine fadeout () to complete the job: When called, it gradually disappears an image within a second on the screen. When the screen turns black, it returns. The features of fadein () are somewhat similar to those of fadein. Waintforinput () is just a simple wait until the key is pressed. It may save the input value to the global variable. Currently, DOS-based games are a very suitable method. In Windows, the Gaming methods are completely different.

Why? Good. What will happen when new_screen is true? It fades the screen, loads the image, and then fades the screen. All processes take two seconds. There will be no messages generated in these two seconds, so the user can minimize the window, but this program is always running, just as the user has not minimized. In this case, errors may occur, and protection errors may occur. Needless to say, this is not desirable. Waitforinput () is worse because it makes every frame of the procedural Program suspended until a key is pressed. The above example has the potential trouble, which can be determined.

In the last line, if your program can reach a frame rate of 30, make sure that your main loop can be executed 30 times per second. Each execution body in the main loop only displays one frame. Theoretically, not each frame calls fadeout (). The first time I learned windows programming, these may be difficult, because this thinking is very difficult. However, once you create a program using this method, you will find that it helps you write a well-organized and flexible program.

Conclusion

This is the basis of Windows programming. The example in this article does nothing except display a window. It contains the entire framework of the Windows application. Next, I will introduce the processing resources, and allow you to integrate your custom image, optical image, audio and audio into your. EXE file.

If you have any questions or suggestions, please contact me via email: ironblayde@aeon-software.com, ICQ: 53210499. See you next time!

Joseph "ironblayde" Farrell
Aeon Software

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.