Game Programming Guide
A guide to game programming
V1.10alpha
Finally updated in 2003.1.14
This article is based on VC7.0 / DirectX 9.0 / Winsock 2.2
Recommended Word 2000 and above
After reading, if you have any comments and suggestions, please be sure to make a message book, thank you! ! !
If you think anywhere is wrong, please tell me ...
If you think anywhere is difficult to understand, please tell me ...
If you think this article is not too rubbish, welcome to your friends? ...
99% of this article is original content, please only give a connection when reprint, thank you!
I also hope that everyone should not modify, thank you!
Use "View" ---- "Document Structure Map" can make great convenience to read this document
Bloomberg
By Peng Bo
Email: Kane_PENG@netese.com
QQ: 4982526
http://www.kanepeng.com
table of Contents
Game Programming Guide 1
Directory 1
Guide 1
Chapter 1 shows the language of the game 1
1.1 VC.NET Overview 1
1.2 Getting Started 4
1.2.1 Number and Data Type 4
1.2.2 Variables and constant 4
1.2.3 Namespace 5
1.2.4 Operators and Expression 6
1.3 Precomimination Directive 7
1.4 Structure, Joint and Enumerated 8
1.4.1 Structure 8
1.4.2 Union 9
1.4.3 enumeration 10
1.5 control statement 10
1.5.1 judgment and jump statement 10
1.5.2 Select Statement 11
1.5.3 circulating statement 13
1.6 function 13
1.7 pointer, array and string 17
1.7.1 pointer 17
1.7.2 array 19
1.7.3 String 22
1.7.4 small junction 23
1.8 multi-file structure 23
1.9 Common Function 25
Chapter II how to say more authentic 29
2.1 Define and use class 29
2.2 Class constructor 32
2.3 static member 34
2.4 Operator overload 35
2.5 inheritance 38
2.6 virtual functions and abstract class 41
2.7 Template 42
2.8 Optimization Procedure 45
2.9 debugging procedure 47
Chapter III Space 49
3.1 Basic Windows Programs 49
3.2 WinMain function 53
3.2.1 Introduction 53
3.2.2 Registration window 53
3.2.3 Creating Windows 55
3.2.4 Display and Update Window 56
3.2.5 Message Cycle 57
3.3 Message Processing Function 58
3.4 Common Windows Functions 59
3.4.1 Display dialog 59
3.4.2 Timer 59
3.4.3 Get time 60
3.4.4 Playing Sound 60
Chapter 4 draws the brush 61
4.1 Initializing DirectDraw 61
4.1.1 Introduction 61
4.1.2 DirectDRAW Object 62
4.1.3 Set control level and display mode 63
4.1.4 Creating a page 64
4.2 Background Cache and Page 66
4.3 Turn into image 67
4.4 Pages Loss and Recovery 67
4.5 Transparent Color 68
4.6 Image Transfer 68
4.7 Program Example 72
4.8 Image Zoom 72
4.9 Release DirectDraw object 72
Chapter V Fluor Skills 74
5.1 Fill color 74
5.2 Output Text 75
5.3 GDI drawing 75
5.4 Program Example 76
5.5 Lock page 76
5.6 program speed 78
5.7 Special Effect 82
5.7.1 Daruting and highlighting 82
5.7.2 fade in fade out 83
5.7.3 Translucent 83
5.7.4 Light 84
5.7.5 Dynamic Light 85
5.7.6 Light System 88
5.7.7 Weather Effect 88
Chapter 6 Accelerating Games Magic 89
6.1 Introduction to the embedded assembly 89
6.2 Basic Directive 90
6.3 Arithmetic Directive 91
6.4 Logic and Displacement Directive 936.5 Comparison, Test, Transfer and Cycle Directive 93
6.6 Basic Directive for MMX Directive Sets 96
6.7 MMX Directive Set Arithmetic and Comparison Directive 98
6.8 MMX instruction set logic and shift command 99
6.9 MMX Instruction Set Format Adjustment Directive 100
Chapter 7 I didn't want to have a good name 102
7.1 Read keyboard data 102
7.2 Read Mouse Data 103
7.3 Recovery and Turn DirectInput 104
7.3.1 Restore DirectInput Equipment 104
7.3.2 Close DirectInput 104
7.4 Initialization and Turn DirectX Audio 104
7.4.1 Initializing DirectX Audio 104
7.4.2 Close DirectX Audio 105
7.5 Playing MIDI and WAV Music 105
7.5.1 Turn into MIDI and WAV files 105
7.5.2 Playing MIDI and WAV files 106
7.5.3 Stop playing 107
7.6 Playing Music 107 in 3D Space 107
7.7 Playing MP3 Music 109
7.7.1 Turn into MP3 file 109
7.7.2 Play MP3 file 109
7.7.3 Stop playback and release object 110
Chapter 8, the cornerstone 111 supporting the game
8.1 Link Watch 111
8.2 hash table 111
8.3 Rapid Sort 112
8.4 Depth Priority Search 113
8.5 Guangsheng Priority Search 117
8.6 Heuristic Search 120
8.7 Dynamic Planning 126
8.8 Neural Network 128
8.9 Genetic Planning 129
Chapter IX Moving 3D World 131
9.1 Overview 131
9.2 Basic Knowledge 133
9.2.1 Initializing Dxgraphics 133
9.2.2 Close DXGraphics 135
9.2.3 Recovery DXGRAPHICS Equipment 135
9.3 Setting Scene 135
9.3.1 Setting the rendering status 135
9.3.2 Setting Matrix 136
9.4 Creating a Scene 137
9.4.1 Turn to 3D Scene 138
9.4.2 Turn to 2D Image 139
9.5 Refresh Scene 140
9.6 Rendering Scene 141
9.6.1 Rendering 3D Scene 141
9.6.2 Rendering 2D Image 141
9.7 Change Scene 141
9.8 Display Text 142
9.9 Program Example 143
Chapter 10 I didn't want a good name 144
10.1 Light 144
10.2 Translucent 145
10.3 Texture Mix 146
10.4 fog 148
10.5 Confucius stickers and environment map 149
10.6 particle system 149
10.7 Skeleton Animation 149
10.8 mirror 151
10.9 shadow 151
Chapter 11 I didn't want a good name 152
11.1 Basic Concept 152
11.2 Procedure 152
11.2.1 Server End 152
11.2.2 Client 153
11.3 Program Example 153
11.4 Error handling 158
11.5 Display IP address 158
11.6 Transfer data more efficiently 159
Chapter 12 Creates our world 161
12.1 Procedure Process 161
12.2 Program Structure 162
12.3 Basic Method 163
12.4 SLG programming points 163
12.4.1 Computer AI 163
12.5 RPG & ARPG Programming Points 163
12.5.1 Generation of Maze 163
12.5.2 Script Technology 163
12.6 RTS Programming Points 163
12.6.1 Looking for Road 163
12.6.2 Computer AI 163
12.7 FPS Programming Point 164
12.7.1 Mobile 164
12.7.2 Collision detection 164
12.8 Physics in the game 165
Audio 166
Appendix a WINDOWS Common Message List 166
Appendix Bi Virtual Key List 171
Virtual key in Windows Message 171
Virtual key in DirectInput 172
Appendix three DirectX function returns list 174
DirectDraw section 174
Direct3D section 181
Appendix 4 WINSOCK Function Return Value List 183
Appendix Five Game Programming Common URL 187
Appendix Six Chinese and English Name Control 188 Appendix Seven FAQs and Solutions 189
1. "Warning" 189 appears when compiled
2. "Cannot Execute Program" 189
3. "UnreSolved External Symbol" 189
4. Running error 189
5. What else is there, you can tell me 189
Guide
Before you start reading the full text, I hope you can take some time to read the content here ...
First, what kind of game do you want to compile?
(1) Star-fighting, Empire, Heroes invincible, big rich 4, Xuanyuanjian 3, legend, stone era ...
These are authentic 2D games, their logo is: the perspective is completely fixed or only four observation direction. There are not many special effects in these games, even if you don't need to use assembly to accelerate.
Recommended reading: Chapters 1, 2, 3, 4, 5 and Chapter 12.
Optional reading: Chapter 7, 8. If you need a network function, you need to read Chapter 11.
(2) Diablo 2, Qin ...
This is a relatively special 2D game, which is characterized by a large-scale use of various special effects (translucent, light and shadow effects, etc.). Some such games can also use a 3D acceleration card to accelerate 2D effects.
Recommended reading: Chapters 1, 2, 3, 4, 5, 6 and Chapter 12.
Optional reading: Chapter 7, 8, 9, 10. If you need a network function, you need to read Chapter 11.
Since the current graphics card can hardly support 3D acceleration function, if you intend to give up on computers without a 3D accelerator card, you can read Chapters 4, 5, 6, and recommend reading Chapter 9 and 10 Section 1, 2.
(3) Counter-Strike, Thunder, Warcraft 3, Dungeon Siege, FIFA, Need for Speed, MU ...
These are pure 3D games, but also represent the development trend of the current game.
Recommended reading: Chapters 1, 2, 3, 7, 9, 10 and Chapter 12 of Chapter 12.
Optional reading: Chapter 8. If you need a network function, you need to read Chapter 11.
Chapter I show the language of the game
I want everyone to hear "Computer Language", we have to express our game to the computer to express our games - this process is the so-called "programming". Since the game's speed is high, we generally use the C language because the procedures prepared to use it not only perform speed, but also the various resources of the hardware. And now (but it is also more than ten years ago?) Has a C language, which is a major improvement to the C language. The biggest feature of the C language is to provide "class" and become "object-oriented" language. About this, we will introduce in the second chapter. This chapter will introduce some of the C language basic knowledge necessary for some game programming. Finally, I need to remind everyone that it is best to learn to practice while learning this chapter, try writing C programs.
1.1 VC.NET overview
We need to briefly introduce the basic use of VC.NET before cutting into the C language. First of all, of course, it is to install VC.NET, it is worth noting that the DirectX SDK attached in VC.NET is not an 8.1 final version, and it is recommended to access http://www.microsoft.com/msdownload/platformsdk/sdkupdate/ update. Then start VC.NET, you will see "Start Page", select Visual C Developer "in the" Profile "column. The second step is to go to the "GET STARTED" column, select "New Project", and select "Win32 Project" in the "Visual C Projects" column in the window, fill in "name" and "location", press "OK", there will be a "Win32 Application Wizard". At this point, you need to hook the box before "Empty Project" column, to prevent VC.NET to join some meaningless garbage in the project; if you want to edit the program under the DOS window, for example this chapter And the procedure of the next chapter, you have to select "console application". Finally, press "Finish" to complete the creation of the project. Figure 1.1 Start Page
On the left side of the screen, we can see the "Solution Explorer" and "Dynamic Help" two columns, where "Solution Explorer" can be changed to "class view" or "resource view", and its content is: project included What documents, the structure of the class, variables, and functions in the engineering, what resources are included in the project. As for "Dynamic Help" is dynamic, it is very convenient.
Everyone will notice that there is no file yet, so we need to learn how to create a file. If you want the new file being a C program file (.cpp), then press Right-click on "Source File", select "Add "-" Add new item ", select" C file "in the window, set the name, then press" Open "(if you join" Haha.cpp ", Solution Explorer will be as shown Disted); if you want the new file being the header (now do not do the header file), press the right button in "Header File", "add" - "Add new item", window appears Select "Header File" and set the name and press "Open".
The mode of the program can be changed in the middle of the toolbar: Debug or Release. In general, it is recommended that you choose Release to reduce file size and increase running speed; when debugging, you must select Debug. By default, compiled programs will be placed in the "debug" or "release" subdirectory under the project directory. Finally, let's take a look at an important operation, how to put the lib file (now not to manage the lib file ...) Join the project: First find the project name in the "Solution Explorer" window, then press the right
Figure 1.2 key and select "Properties", select "Linker" in the window appear - "INPUT" --- "Additional Dependencies", finally fill in the lib file name to join.
OK, let's take a simple C program:
/ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
First C Program
-------------------------------------------------- * /
#include
// Now you only need to know that you must write this line to use the input and output statement.
// This line does not have to add a dividend number because it is not a real C statement.
// will explain this in Section 1.3.
Using namespace std; // What does this mean ... there is an explanation at 1.2.3.
INT A = 5; // Declare the variable A, and fly 5 to it. The variables in C must be used first.
// INT illustrates the data type of A as an integer.
INT Square (int x); // Declaration function Square, it has a parameter, an int type, ie integer. Return value
/ / Is an int type. The functions in C need to be defined first after declaration.
The int Square (int x) // function is really defined.
{
Return x *
X; // Return x * x, you can insert it into a few rows in the interval position in one statement.
}
INT main () // main function, the C program under each DOS window requires it
{
INT A; // Declare variable A. The location of the variable declaration in C is relatively casual.
Cout << "Please enter a:"; // Output "Please enter A:", the direction of the arrow is very intuitive.
CIN >> A; // Enter a change in the direction of the arrow.
COUT << "a =" <
<
(3) The sentence enclosed by "{" and "}" is called a block statement, and the form is considered to be a statement (just like Begin and End in PASCAL).
(4) "//" to the end of the note, "/ *" to "* /" is all annotated, they will not be compiled.
(5) The body is made up of a function. The function will be described in detail in the 1.6 section.
1.2 Getting Started Knowledge
1.2.1 Number and Data Types
For the decimal representation, C is consistent with other languages, and the scientific counting can be used, such as 3.145e-4. In C , you can also directly represent the number of hexadecimal numbers, as long as "0x" is added in front. Such as 0x23. If you want to indicate a negative hexadecimal number, you can add a negative sign before "0x". For clarity, a number is a Float type, we can add "f" at the end of the number, such as 1.00F. Otherwise, the number is the Double type. Let's take a look at the basic data type in C :
BOOL (logical) char (character or 8-bit integer) short (16-bit integers)
INT (16-bit or 32-bit integer) Long (32-bit integer) Float (32-bit floating point)
Double (64-bit floating point) long double (80-bit floating point)
The Bool type is true and false with true and false representative, and its actual space is 8 bits.
A CHAR type variable is equal to 'a' (note that characters in C use single quotes, with double quotes), then it is equal to A's ASCII code, ie 97. So on and so forth.
The int type is generally 16 in DOS. Usually 32 in Windows, if you want to be insured, you will try it.
In front of the integer data type, "unsigned" can be added as unsigned as unsigned, and the range can be doubled. For example, the range of CHAR type data is -128 to 127. The range of unsigned char type data is 0 to 255.
Using sizeof () can get the number of bytes occupied by any object, for example, if there is a CHAR type variable A, SIZEOF (A) will return 1.
Some types can be automatically converted. If the value of a FLOAT type variable can be assigned to a variable of an int type, the portion of the decimal point will be automatically cut off. If you don't assured, you can use the forced type conversion, the form is (Target Type) Variable Name. For example, if there is a CHAR type variable C value is 'b', the direct output C will get the 'b' this character but output (int) C will get the ASCII code of 'b'. Forced type conversion does not change the value of the variable (unless a floating point number is converted to an integer, it is just returns the value after the conversion. Note that the string and integer cannot be converted with a mandatory type conversion, and the approach is from 1.9.
We can also define your own data types with typedef, such as typedef myint unsigned int; after Myint is equivalent to Unsigned Int. The VC.NET system has pre-defined a lot of types, such as the Unsigned Char, Word equivalent to Unsigned long, etc.
1.2.2 variables and constants
The variables in C can be defined anywhere, and multiple variables can be defined simultaneously, such as Int A, B ;. But each variable only works in the most close to its pair {and} symbols, only the variables defined outside of all functions are global variables, which is valid throughout the CPP file. If local variables and global variables are called, local variables are used when they are called. If you must use the global variable, add "::" before the variable is called. It is recommended to use less global variables as possible because it may make the program chaotic and difficult to debug.
All variable definitions can be added to the modifier "const" to indicate that it is constant and cannot change its value in the program. In fact, if we don't intend to change a variable in the program, we can declare it as constants to prevent accidental changes. We can also add modifier "static" to indicate that this variable is static variable. This is to give an example to make it easy. For example, there is such a definition in a function: static int count = 0; then Will open a fixed space for COUNT variables and set the initial value of count. When this function is executed in the future, the program does not allocate space as a normal variable, which will not change its location and value. In other words, its life cycle is the same as the entire program. This will count how many times the function is executed by adding a count = count 1 in a function. 1.2.3 namespace
Namespace is a very interesting thing, it is introduced to make it easy for us to use the same name, constant, class (in the second chapter we will contact the class) or function. A NameSpace is defined in this:
Namespace xxx // xxx is the name of Namespace
{
Here you can define all things like usual.
}
In the future, you should use something in a namespace, such as the AAA in XXX, like this: xxx :: aaa. But this seems to be troubles - there will be a "xxx ::" in a white. So I have "Using Namespace XXX;" this statement, you can help you save these characters. Remember, "Using Namespace" is just a role in the most close to its pair of {and} symbols, which is valid throughout the file. note:
Namespace S1
{
INT A = 0;
}
Namespace S2
{
Float a = 0;
}
void main ()
{
Using Namespace S1;
Using Namespace S2;
// a = a 1; // This sentence is wrong! Because the compiler will not be able to determine a Namespace in this at this time.
S1 :: A = S2:: A 1; // This is correct
}
So why we want usning namespace std in the first program? In fact, it is also to turn "std :: cout" into a simple "cout". Please see 1.3.
1.2.4 Operators and Expressions
The last thing to say is the operators and expressions in C . If you don't have the same language as other languages, there will be some different languages that are different from other languages:
% Is to take the remainder, such as 20% 3 = 2.
In the logical expression, use == to indicate equal,! = Indication is not yained, for example, (4 == 5) is false; greater than or equal to> = indication, less than or equal to <=. && represents logic and, || Represents logic or, "indicates logic. For example, if A = 8, ((a! = 9) && ((a == 3) || (a == 6))) is false.
<< (left shift) and >> (right shift) are very easy to use, the effect is to shift the number of binary forms to the left or right (<< and >> in CIN and COUT use operator overload, so the meaning Different, specifically refer to Section 2.4), two examples may be well explained:
18 (binary form is 0010010) << 2 Get 72 (binary form is 1001000) 77 (binary form is 1001101) >> 3 Get 9 (binary form 0001001)
We can see that left shift and right shift can replace or divide the 2 N-moving role, and do this can save a lot of CPU operations. This approach is important in program optimization, such as A * 9 available (a << 3) a replace (note, " " operation is preferred than "<<".
C also provides an important binary operation for arithmetic and &, arithmetic or |, arithmetic non-~, arithmetic expressions or ^. For example, 25 (11001) ^ 17 (10001) is equal to 8 (01000). These calculations are done in one position. 0 & 0 = 0, 0 & 1 = 0, 1 & 0 = 0, 1 & 1 = 1; 0 | 0 = 0, 0 | 1 = 1, 1 | 0 = 1, 1 | 1 = 1; ~ 0 = 1, ~ 1 = 0; 0 ^ 0 = 0, 0 ^ 1 = 1, 1 ^ 0 = 1, 1 ^ 1 = 0.
/ - operator, that is, from 1 / self-reduction 1, is one of the characteristics of C . A = 7; A ; then A becomes 8. (C language is not a D language?) Note A and a different: A is given first afterwards, A is the first to increase the value: if A = 12, ( ) 5 It is 18, ( a) 5 is 18, but A later turned 13.
Finally, it is a very interesting operator, "?:", It can replace the role of the IF statement to a certain extent, because "a? B: c" is equivalent to "if a life returns B ELSE Return C ". For example, (a> b)? A: B can return a larger in A and B.
It is worth noting that because C operators are numerous, the operations have been more complicated. If this does not pay attention to this, there will be few parentheses will appear unexpected results. The operators in C are listed below by high priority to low:
1. () (parentheses) [] (array subscript). (Member of the class) -> (member pointing to the member)
2.! (Logic is not) - (Number) (plus 1) - (1) & (Variable Address)
3. * (The content refers to the contents of the pointer) SIZEOF (length calculation)
4. * (multiply) / (remove)% (mold)
5. (plus) - (reduction)
6. << (bit left) >> (right shift)
7. <(less than) <= (less than or equal)> (greater than)> = (greater than or equal)
8. == (equal to)! = (Not equal)
9. & (bit)
10. ^ (bit or)
11. | (bit or)
12. && (logic and)
13. || (logic or)
14.?: (? Expression)
15. = = - = (Joint operation)
In terms of expression, C is basically the same as other languages. Just c for simplification, providing joint operations: "Left value = expression" equivalent to "left value = left value expression". For example, a * = 6 b is equivalent to a = a * (6 b), C = 8 equivalent to C = C 8.
In C , all expressions have a return value. In general, (left value operator right value) The return value of the expression is the same as the right value; the return value of the conditional expression such as (a> B) is 1 when the condition is established, and is 0.1.3 precompiled instructions when it is not established.
Now this explanation in the first example #include
The meaning is, in fact, this is a pre-compiled instruction. The pre-compiled instruction indicates that the operation proceeded by the compiler before the program is officially compiled, which can be placed in any location in the program. Common pre-compilation instructions are:
(1) #include
instruction
This instruction indicates that the compiler inserts all the contents of the xxx.xxx file into this. If you use <> to find a file in the system's include directory, if you enclose a file, you look for files in the current directory. Generally, the file is "H" or "HPP", which is called header file, which is the statement of various things.
So why we can omit "iostream.h" in the first program (everyone can find it yourself, will find that there is no file called Iostream)? This has a little story. At the beginning, ANSI had made some modifications to Iostream.h when normalizing C , such as all of them put into a namespace called STD (there are many headers to be modified). But the programmer did not agree, because this means that their programs must be modified to adapt to the new compiler. So ANSI had to retain the method of calling iostream.h to the original (#include)
Support, modify the new iostream.h method to the present (#include)
).
Master, our #include
The compiler will then see the declaration of the input and output function in iostream.h, so you know that you want to use these functions, you will include the library file that includes the input output function and the compiled product, forming an executable .
Note <> Do not search the header file in the current directory, if we don't use <> to expand the header file name, its meaning is to search the header file in the current directory, and search it in the system default directory.
(2) #define instruction
There are three usage of this instruction. The first is the definition identifier, the identification is valid for the entire program, the shape is as #define xxx, often cooperate with the #IF; the second is to define a constant, such as #define max_sprite 100, Max_Sprite represents 100 (It is recommended to use const as possible to use const as possible); the third is to define "functions", such as #define get_max (a, b) ((a)> (b)? (A): (b)), then use GET_MAX ( X, Y) You can get X and Y, some of the draws, such as GET_MAX (A , B), how many times the A will be implemented depends on the size of A and B. So everyone still uses inline Function instead of this method improves speed. For functions, please refer to 1.6. But this method is indeed very flexible, because A and B can be a variety of data types, this feature we can switch the template implementation in Section 2.7).
(3) # IF, # else and #endif instruction
These instructions generally cooperate with:
#if defined (ID) // If the identifier is defined
Instruction to be executed
#ELSE
Instruction to be executed
#ENDIF
In the header file to avoid duplicate calls (for example, two headers contain each other), this is often used: #IF! (Defined xxx) // xxx is a unique identifier in your program,
/ / The identifier of each header should not be the same.
// The common method of the supersonics is that the number file name is "abc.h"
// identifically as "abc_h"
#define xxx
Real content, such as function declaration
#ENDIF
1.4 Structure, Joint and Enumeration
1.4.1 structure
The structure can turn different variables to a member of a variable. E.g:
Struct S / / Define Structure S
{
The first member of Short Hi; // Structure S
Second member of Short Lo; // Structure S
}
S S; / / Define the variable S type S
Then we can use the members of S like S.HI and S.LO.
1.4.2 combination
The combination allows different variables to share the same space, and take an example:
#include
Using namespace std;
Struct S
{
Short hi;
Short Lo;
}
Union Mix
{
Long L;
S s;
CHAR C [4];
}
void main ()
{
MIX MIX;
Mix.l = 100000;
Cout <
<
Cout <
}
The case where the memory location of MIX is at this time is like this:
Figure 1.2
1.4.3 enumeration
The use of enumeration is quickly defined a large amount of constant. E.g:
Enum year // can give a name to enumerate
{
January = 1, // If you don't add "= 1" will be 0-11 in turn, it does not meet our usual habits, so
/ / Can add it.
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December
}
1.5 control statement
The control statement format in C is simple and powerful, fully proves that it is a programmer's language.
1.5.1 judgment and jump statement
The judgment statement format in C is as follows:
IF (condition) Implement statement; Else Execute statement;
E.g:
IF (a> = 9) A ; ELSE A -;
It is worth noting that the meaning of "true" and "false" in C is that this expression is not 0 or is 0. For example, if (a-b) DO_STUFF; the effect is with IF (a! = B) DO_STUFF; the same.
The notorious jump statement (but sometimes you still have to use) is like this:
Number: statement; (generally the beginning "_" start)
Goto label;
For example, it is convenient for everyone to understand:
#include
Using namespace std;
void main ()
{
INT TARGET = 245; int A;
Cout << "Welcome to play this boring guess game" <
Cout << "Your goal is to guess the number I think about" <
Cout << "Please enter the first guess number:";
_INPUT: CIN >> A;
IF (A> Target)
{
Cout << "The number of you just entered is too big!" <
Cout << ";
goto _input;
}
Else IF (a
{
Cout << "The number of you just entered is too small!" <
Cout << "guess again:";
goto _input;
}
Else
COUT << "Congratulations, guess it!" <
}
1.5.2 Select statement
The selection statement in C is flexible, let's take a look at the form similar to other advanced languages:
Switch (variable)
{
Case constant / constant 1:
Statements; // Note, there can be multiple statements here without having to enclose {}, but variables cannot be defined.
Break; // Why do you have this sentence? The following will be explained.
Case constant / constant 2:
Statement;
Break;
......
Case constant / constant N:
Statement;
Break;
DEFAULT: // If all conditions are not met, the statement here is performed.
Statements; // There is no need to add BREAK.
}
Break's role is actually to prevent continuing to perform the following statement, try the procedure below:
#include
Using namespace std;
Const aaa = 5;
void main ()
{
Int a;
CIN >> A;
Switch (a)
{
Case 0:
Cout <
<< "You entered 0";
Case 3:
Cout <
<< "You entered 3";
Case AAA:
Cout <
<< "The number of you entered is equal to AAA";
DEFAULT:
Cout <
<< "???";
}
}
According to the average people's idea, when you enter 0, 2, 3, 5 will get "You Enter 0", "???", "You entered 3", "The number you entered is equal to AAA" But if you can try the results, it is true. After the test, you can add some Break to see what the results will be.
1.5.3 circulating statement
First introduce the While loop statement, a total of two forms: The first is the While statement, meaning whether the judgment condition is met, if the execution statement (otherwise exit loop), then repeat this process. The second form is the DO statement while (condition), meaning is the first execution statement re-judgment condition, and if the condition is established, the statement continues to perform the statement (if it is not settled), this process will continue to repeat. For example, while ((x = 1) = Y); the statement can make X constantly add 1 until the same is the same as Y.
Then it is C most powerful for loop, its form is as follows:
FOR (Statement 1; Condition; Statement 2) Statement 3 (any part of which can be omitted)
It seems that it seems to be quirky, in fact it is equivalent to this:
Statement 1;
WHILE (Condition)
{
Statement 3;
Statement 2;
}
For example, for (i = 1; i <= 100; i ) cout <
<
Also, for example, for (CIN <
For (;;); will fall into the dead cycle, pay attention to it than the while (1); execution speed is fast.
Define variables by way in circulating statements, such as for (int i = 1; i <= 100; i ) cout <
<
Sometimes we need to jump to the loop in the loop. At this time, Break can also be used in the field. Sometimes you need to jump to the next loop in the loop, Continue can help you.
1.6 function
The function in C is defined:
Return value data type function name (parameter table)
{
Statement;
}
E.g:
Int Fun (int x, int y)
{
X = x 1; // Note that this sentence can only change the value of X in the function, see Return x * y; // returns x * y and will immediately exit the function
}
When the returned value data type is VOID, there is no return value, just like the "process" in other languages.
In the case where the parameter table does not cause ambiguity, there is a default value, such as Void XYZ (INT A, INT B = 0); (I only need to illustrate the default value when the declaration function is required), then XYZ (12) is equivalent to XYZ (12,0).
It is best to declare the function in the program before the main function begins (the main function does not state), the declaration format is:
Return the value of the data type function name (parameter table); (note having a semicolon)
The variable name can be omitted in the statement table, such as Void MyFunc (INT, FLOAT);
The front plus "inline" in the definition of the function (not a declaration) indicates that it can improve the inline function, but increasing the size of the file.
Like other languages, the functions in C can recursively call (call themselves themselves). It also has an important feature distinguishably in other languages --- can "overload", for example, if there is such two functions:
Float Fun (Float X)
{
Return x * x * x;
}
Int Fun (int X)
{
Return x * x;
}
Suppose A is 4, then if a is a FLOAT type variable, FUN (a) will return 64; but if a is an int type, Fun (a) will return 16. You can imagine that this feature will be useful in actual programming.
Let's take a look at the question: Some people want to compile a function of exchange A and B, so he wrote this:
Void SWAP (Int A, INT B)
{
INT T = a;
A = B;
B = T;
COUT << "a =" <
<< "Void Swap (INT & A, INT & B) {INT T = A; A = B; B = T;} By default, the return value of the function is just a replica, if you must let it return to true Things, you can write functions like this: int & foo () {do_something;}. However, pay attention to the limitations described in the 1.7.1 section - we can't return to the variable created in the function. One will be used in the following functions Procedure example (more bored?): #Include
Using namespace std;
Float pi = 3.14159;
FLOAT S_CIRCLE (Float R);
FLOAT V_CYLINDER (Float R, Float H);
FLOAT V_CONE (Float R, Float H);
Float V_all (Float Stop, Float Smiddle, Float Sbottom, Float H);
Float v_all (Float Stop, Float Smiddle, Float Sbottom, Float H)
{
Return (STOP 4 * Smiddle Sbottom) * H / 6;
}
FLOAT V_CONE (Float R, Float H)
{
Return S_Circle (R) * H / 3;
}
FLOAT V_CYLINDER (Float R, Float H)
{
RETURN S_CIRCLE (R) * h;
}
FLOAT S_CIRCLE (FLOAT R)
{
Return pi * r * r;
}
void main ()
{
Float r, h;
Float ST, SM, SB;
Cout << "This very boring program will help you calculate some geometric volume" << "0 representative to calculate the cone" < COUT << "1 means to calculate the cylinder" < COUT << "2 representative to calculate the column" < COUT << "Please select:"; Int kice; CIN >> CHOICE; Cout < Switch (choice) { Case 0: COUT << "bottom radius =?" CIN >> R; COUT << "High =?"; Cin >> H; Cout < << "Volume =" < Cin >> H; Cout < << "Volume =" < CIN >> SM; COUT << "The area of the lower surface =?"; CIN >> SB; COUT << "High =?"; Cin >> H; Cout < << "Volume =" < By the way, we should never return the address or reference of the variable created in the function. Because all variables created in the function body are destroyed when exiting a function, although the address can be passed back, the content it is pointed is meaningless. So what? Can I pass back in the function of New? The answer is OK, however, you must be responsible for the most likely memory leaks, because you have to find these pointers a lot of delete. "We can try the address of the static variable or reference", some people will think so. This is a good way in most cases, but there is still possible vulnerability - because the address of this static variable is unchanged by the beginning to the end. as follows: INT & FOO (INT A) { Static Int T; T = a; Return T; } int main () { ... IF (foo (1) == foo (2)) // This condition will be established! ... } What is the use of pointers? The first use is to dynamically allocate a large amount of memory. We know that many languages under DoS have a strict limit on the size of array, but C can open up a very large array, and you can use it to release memory, which is the merge of the pointer. Specific will be introduced when the array is introduced. We can also create a function pointer, which is also one of the features of C . The so-called function pointer, as the name suggests point to a function of a function. For example, if there are some functions: Float AAA (INT P); Float BBB (INT Q); Float CCC (INT R); Then we can define a function pointer this: float (* p) (int); At this time, P point to each function above, such as P = BBB; after execution, P (100); is equivalent to BBB (100); If you need to call AAA, BBB, CCC functions in a segment (such as a variable), then we can use the cumbersome Switch, just use the function pointer array, it is very convenient. By the way, how should I use the data type of P in typedef: typedef float (int); after using PFunction P directly, the pointer P is defined above. 1.7.2 array The arrays and pointers in C have thousands of hints. Like other languages, C can directly define an array, such as INT A [100]; can define an array consisting of 100 CHAR type variables; or by the way by defining, such as Char B [5] = {'a ',' b ',' c ',' f ',' z '} ;; also defined high dimensional arrays, such as Char C [200] [50]; (equivalent to C (200, 50) in Basic) . Pay attention to a few points when using arrays: (1) The subscript of the array begins with 0, the subscript of the A array defined above is 0 to 99, just 100 elements. (2) The array crosstion will not have any tips. (3) The array requires yourself clear. If you use a direct defined method to generate an array, pay attention to the following two points: (1) The magnitude of the array must be constant or constant, like Int a; int b [a]; this is wrong. (2) You get a special pointer as the "array name". The second point may be some enough, you can try this program, you will understand: #include Using namespace std; void main () { INT ABC [1000] = {0}; // This can make the array to be pre-equalized 0 // Note Int ABC [1000] = {1}; will make ABC [0] = 1 and other elements = 0 ABC [0] = 987; COUT << * ABC < * ABC = 787; Cout < } We can also use the pointer to create an array. For example, we have to temporarily allocate a piece of space, store 100,000 int type data, then you can do this: int * p; p = new int [100000]; (You can merge it into int * p = new int = 100000] A new operator "new" appears here again, the system finds a large enough free space, and point P pointing P to the starting position of this space, you can use the P as an array as an array. . The first advantage of this method is to release the memory after using this memory (in fact you should always do so, otherwise it will cause so-called Memory Leak, ie, the memory resource leakage, just like this: delete [] P; ("Delete []" is also an operator in C ); the second advantage is to dynamically define an array, for example: Int a; cin < Therefore, it is recommended that you use new to create an array. However, it is not convenient to use the pointer just used to create an array (try P = & P [100] can achieve P pointing P [100]? Maybe crash!), The most flexible method is to point another pointer to An array of elements because the pointer can be subjected to addition and decrease. For example, if P = a [0], P = 46 can be performed; if P points to A [46], then P - is executed, and P points to a [45]. Take a look at the example below: #include Using namespace std; void main () { INT * P, * Q; P = new int [100000]; Q = & P [0]; For (int i = 0; i <100000; i ) * (q ) = 0; // can also be cleared Q = & P [1]; * q = 128; // turn P [1] into 128 Cout < Delete [] p; // Delete array with delete [] delete q; // Remove pointer to delete // to develop a good habit that is released using the pointer } Sometimes you may forget that I have released a pointer and still use it, and the result will appear unpredictable results. In order to prevent this, you can set it to null after releasing the fingertone to ensure that it is not continued. We can use this two macros: #define Safe_Delete (p) {if (p) {delete (p); (p) = null;}} #define safe_delete_Array (p) {if (p) {delete [] (p); (p) = null;}} Here's also to use the pointer to create a high-dimensional number of high-dimensional groups, because the pointer to the pointer is to be used (the pointer is also variable, but also in memory, so there is a pointer to the pointer of the pointer ... (Ah) One listener fainted! Who lifted him out?) A program below demonstrates how to create a high-dimensional number of P [40] [60] (more difficult to understand, prepare for mental preparation): INT ** P; / / Pointer to the pointer! P = new int * [40]; // After execution, P is an array of elements as a pointer! // You can think about this and p = new int [40]; For (int i = 0; i <40; i ) p [i] = new int [60]; / / to allocate memory for each pointer in the P array, which also becomes an array Below is a structure of a two-dimensional array p [n] [m]: Figure 1.4 If you understand the above program, you can play some new tricks: Define an asymmetric array. For example, this: INT ** P; * p = new int * [10]; For (int i = 0; i <10; i ) p [i] = new int [i 1]; 1.7.3 string The string in C is actually one of the pointers, because there is no basic data type is a string. The so-called string is actually a symbol that is "/ 0" (this is called escape character, representing an ASCII code 0 symbol As a character pointer (char *) as an end sign, it is actually an array of characters, just like Figure 1.2. So if there is a string S is "ABC", it actually "abc / 0", and sizeof (s) will return 4, not 3. Remember one bit when defining the number of characters. Generally, the string is generally defined by the character pointer: char * str = "muhahaha"; Because there is a const here, we don't have to use Delete [] to release this character pointer. People who have used Basic should pay attention to the string in C and cannot be compared (with == comparing two pointers it only compares whether the point to which the two pointers pointing is the same), assigning each other, adding, and subtraction, these Operation is generally implemented by the string operation function provided by using the system, see Section 1.9. Special pay special attention to the string cannot assure each other, please see the following piece: Char * str = "aaaa"; CHAR * STR1 = "OH"; Str1 = str; // !!! Cout < The output is normal, it seems that we have implemented the purpose of copying strings. However, think about it carefully, what will happen to another pointer to another pointer? Suppose the STR pointer is originally directed to address 0x0048d0c0, and the STR1 pointer points to 0x0048D0BC, then execute str1 = STR; the two pointer will point to the address 0x0048D0c0! C does not make specialization for strings, the assignment operation of the pointer will only simply copy the address, not copy content. To copy content, you have to rely on Strcpy (). 1.7.4 small knot Learn so many C knowledge, isn't everyone a little tired? If you want to be excited, then look at the following program, it can output the first 781 bits of π. #include Using namespace std; Long a = 10000, b = 0, c = 2800, d, e = 0, f [2801], g; void main () { For (; b-c;) f [b ] = a / 5; For (; D = 0, g = c * 2; c- = 14, cout < For (b = C; D = f [b] * a, f [b] = D% - g, d / = g -, - b; d * = b); } The program uses all the means provided by C to simplify the code, including the previous comma (can stand together, the return value is subject to the right statement). It is very amazing, it is worthy of research research. Of course, don't advocate such a coded code! 1.8 multi-file structure I remember that I used to use Visual C in the first time, because I was not very familiar with C , I was not successful for a long time. Later, I gave the program email to a master told him to see where the problem was. After he sent the program back, it was already running. It turned out that he added a statement in my head file. " "extern". What does it mean? I was not clear at the time, because many books did not speak multi-file procedures. But now you want you to understand when you read this section. First let's take a look at the multi-file program to become the entire process of executable: Figure 1.5 We can discover that library files (extensions are lib, actually a special compiled program. The definition of system functions exists in libs, so that you can't see their source code) is in the final connection One step to join the program, each file is also in this step. The role of Extern is to tell the compiler that this variable is declared in other program files. Put this external variable declaration in the header file, then contain this header file in each file, then declare the variable in any file, all files can use this variable. If you do not add externaln, the variables used by each file are not the same name but the content will not be unified. In each file, you also need to declare the function before you can use it. However, it is not necessary to use extern. Finally, let's take a look at an example of a simple multi-file program: / *--------------Main.h-----------------*/ #if! (Defined main_h) #include Using namespace std; Extern Int A; Void print (); #define main_h #ENDIF / * ---------------main.cpp -----------------*/ #include "main.h" Int a; void main () { a = 3; PRINT (); / *---------------function.cpp ----------------*/ #include "main.h" Void print () { Cout < } 1.9 common function A large difference between C and other languages provides a huge library, which can improve your efficiency. Look at it first inside: Int rand (): Returns a random integer. Void Srand (int): Reinitialize the random number generator based on the parameter. INT / FLOAT ABS (INT / FLOAT): The absolute value of the number of returns. Min / Max (A, B): Returns smaller / larger in A and B, defined with #define, you don't have to worry about efficiency. INT ATOI (CHAR * S); and returns an integer converted by the S-string. Double ATOF (CHAR * S); returns a floating point number converted by the S-string. Char * GCVT (Double Num, Int Sig, Char * Str); NUM For the swivel floating point number, SIG is a valid numeric number of converted numbers, STR is the starting point of the target string. The function returns a pointer to the STR. For example, if SIG = 5 So 9.876 will be converted to "9.876", - 123.4578 will become "-123.46", 6.7898E5 is "6.7898e 05". After that Mathematical function inside: Sin, Cos, Tan: Do you know this? ASIN, ACOS, ATAN: Anti-trigonometric function. SINH, COSH, TANH: Double Triangle Function. Log, Log10: Nature and common logarithm. EXP, POW10: The reverse function of the two functions above. Pow (x, y): Returns the Y power of x. SQRT: Open the square root. CEIL: Returns the smallest of the minimum not less than x. FLOOR: Returns the maximum integer that is not more than X. Hypot (x, y): Returns the square of the square plus the square of the x-ray plus. File read and write functions Inside, how to use is: First define the pointer to the file and open the file, such as file * file = fopen ("aa.bbb", "rb");, where aa.bbb is the name you want to open (note if it is developed in VC.NET Press F5 or CTRL F5 to execute the program in the environment, the default file read directory of the program is the project's directory, not the debug in the project directory or the release directory). If there is a path, use "//" or "/" instead"/". "RB" is open mode, and the basic mode has these: Table 1.1 Readable data? Write data? Read the write pointer position if there is no new file when opening a file? R Yes No Document No W No Yes Finad No A No is the end of the file R is the file header No W is the file header A is the end of the file After the basic mode, add B or T to set the binary or text files to open. For the former, we can read the data with FREAD after we open the file, and the read and write pointer will move. The method of fread is: Fread (p, size, n, file);, p is a pointer to the location where the read data will be stored, and the size of each data block is the number of bytes of each data block. Block, File, the pointer to the pointing file just defined. For example, FREAD (& A [0] [0], SIZEOF (A [0] [0]), SIZEOF (A) / SizeOf (A [0] [0]), File); read data into two-dimensional array A . It is necessary to pay attention to the data you get is ascii yard! For example, if the content of the file is "10", you will read the number of 49 and 48 (1 and 0 ASCII code). With FWRITE, you can write data, the form is exactly the same as FREAD, and the method is also the same. For text files, we should use the fscanf () and fprintf () functions to read and write data. These two functions are flexible, let us look at the following program: #include Using namespace std; Char s [5] = "abcd"; INT i = 967; Float f = 3.1415; CHAR C = 'x'; void main () { FILE * file = fopen ("aa.txt", "wt "); FPRINTF (file, "str1 =% s", s); //% S is indicated in this location is a string FPRINTF (file, "/ nint2 =% d", i); //% D represents an integer, / n indicates a wire FPRINTF (file, "/ nfloat3 = / N% f / nCH AR 4 =% C", F, C); //% f indicates the floating point number,% C represents the character, and several fprintf can be written. Fclose (file); } The content of AA.TXT is: Str1 = abcd INT2 = 967 Float3 = 3.141500 CH AR 4 = X The use of fscanf () and fprintf () is almost exactly the same. The only difference is that if you want to read the data into normal variables, add a "&" in front of the variable, so that the FPRINTF can modify the value of the variable. Of course, if you want to read, you don't have to do this. FSeek can move read-write pointers, FSEEK (file, offset, because); File is file pointer, imaging starts location, 0 represents the beginning, 1 represents the current location, 2 represents the file tail. Offset is the number of bytes that require moving. Using ftell (file); you can know the current read-write pointer position (how many bytes from the file header). In fact, there is still a simple and very document reading and writing method (also in line with C standards): #include Using namespace std; Int a; ...... IOFSTREAM FILE; File.open ("abc.dat"); // use file.open ("abc.dat", iOS :: binary); can be specified as binary mode File >> a; // is like cin File << "abcdefg"; // is like cout ...... It is to be said to be a common string function, and there is a definition thereof. Char * STRCPY (Char * DEST, CHAR * SRC);, this function enables DEST = SRC and returns new DEST. Use it to implement the conversion between strings and character arrays. Char * STRCAT (CHAR * DEST, CHAR * SRC);, connect the SRC to the DEST and return to the new DEST. CHAR * STRSTR (Char * S1, CHAR * S2);, return to a pointer to the position of the first occurrence of S2 in S1. CHAR * STRCHR (Char * S1, CHAR C);, returns a pointer to the position of the first occurrence of C in S1. Char * strlwr (char * s);, turn all uppercase letters in S to lowercase. CHAR * STRSET (CHAR * S, CHAR C);, replace all characters in S to character c. INT STRLEN (Char * S); and return the length of the string. Finally Memory function: Memcpy (CHAR * DEST, CHAR * SRC, INT N); Note that the location of DEST and SRCs cannot overlap in memory. Memmove (CHAR * DEST, CHAR * SRC, INT N);, you can also achieve copy, DEST, and SRCs in memory can overlap. Of course, it is slower than Memcpy. MEMSET (S, C, N);, the n bytes starting from S are set to c. Can be used to clear arrays and structures. Chapter II how to say more authentic The largest difference between C and C is that C is an object-oriented language, that is, the program is based on object rather than a function, so strictly, we are discussed in the first chapter, not authentic C program. Class (Class) is a key to object-oriented, and it is a data type and is an expression and abstraction of things. Category has a variety of members, which are data, identifies the various properties of the class; some are functions (functions in the class), indicating various operations that can be performed on classes. For example, we can build a "grass" class, which can have various properties such as "height" and "cut", "watering" and other methods. 2.1 Definition and Use Class Let us first look at a program that uses a class: //---------------------------Grass.h------------------ ----------------- Class gras // Define Grass class { Private: // Statement The following members are private. Outside the function If you try to access, the compiler will tell you that there is a fault. // mistakenly and refuse to continue compilation. Everything in the class is private, so this line can be omitted. INT Height; // In general, all data members in the class should be private / / However, the procedure behind this chapter also has public data members in order to facilitate explanation. Public: // The following members are public, no one can access. Void cut (); Void water (); INT GET_HEIGHT (); Void set_height (int newh); }; // This semicolon should not miss it! //--------------------------------------------------- --------------- #include Using namespace std; #include "grass.h" / / The method of the class is defined below Void grass :: cut () // "::" indicates that CUT () is a member of Grass. { IF (Height> = 10) Height- = 10; // Free access to any member in Grass. } Void grass :: water () { HEIGHT = 10; } Int grass :: get_height () // Don't access Height directly outside the class, so you have to write this function. { Return Height; } Void grass :: set_height (int newh) //, we wrote this function { IF (new> = 0) HEIGHT = NewH; } void main () { Grass gras1, grass2; // actually this sentence and "Int A, B;" there is no difference, think about it! This sentence // sentence is called instantiation. Grass1.set_height (20); // If you use VB, you will feel very kind. Functions other than class even // Yes, it is also necessary to use the public part of the access class. " Cout < Grass1.set_height (-100); // Because set_height makes it a protection, this sentence will not give // HEIGHT a ridiculous value Cout < grass1.cut (); Cout < Grass2 = gras1; // The same object can be directly assigned each other Cout < Grass * grass3; // can also define a pointer to the class Grass3 = new gras; // also wants new Grass3-> set_height (40); // Since Grass3 is a pointer, here is "-> ". In fact, it can also // Use (* grass3) .SET_HEIGHT (40); ("." Operation is more than "*" The // is priority when the operator is executed, but it is more troublesome. Grass3-> water (); Cout < Get_height (); DELETE gras3; // release pointer } Looking at the comment, you should be able to read this program, now we can see the first advantage of the class: package. The package is like the above seems to have a hidden in Xuan deficiency, and write a few functions that seem to be bored and rewrite the functions of Height. However, in the program we can already see this protective data. And in large software and multiplayer, because private members can hide the core part of the class, only the public interface is communicated with other functions, so when we modify the data structure of the class, just change the interface function, other The function can still call the data in the class as before, so that a class can be used as a module, which is conducive to everyone's collaboration and decrease. Some people may think that the write interface function slows down speed, then you can add "inline" before defining it into the inline function. Functions other than the class actually access to the private parts of the class, as long as the statement like "Friend Int XXX (INT XXX, INT XXX)" is added to "int xxx (int XXX) The xxx, int xxx "" function can access the private parts of the class. This function is called a class friend. Attention The function in the class is best not to return a reference or pointer in the class in the class, otherwise we will obviously can force a private member in the class through it Z. In addition to public and private, there are protected permissions, usually the same as private, which will further explain its use when the inheritance of the lecture. In the definition of the class, you should notify the member data when you define member data (as if Int a = 0 is like this), and the member data cannot be illustrated by extern. A class of objects can be used as a member of another class. E.g: Class X { Int a; } Class Y { x b; } If we intermise the two classes of the above two classes, then the X class is not defined because the X B is executed, the compiler will report an error. So how should you solve it? Very simple, add a Class X at the forefront; pre-declare it. The same class can assign each other. Category can be used as an array element. You can define a pointer to the class. In short, the class has a normal data type. As long as you define a class, you can create a batch of objects in large quantities, and the established objects have intuitive properties and methods. This is also one of the benefits of classes. The structure is actually a kind, but the default access rights of the structure are public. Simply change "Class" to "Struct" when defining the structure. Generally, we use the structure only when the data is described, and the class is used in both data, but also to describe the operation of the data. Last introduction to what method can get a variable which class is the object: TypeId (AAA) .Name () can return the name of the AAA variable belonging, pay attention to this is achieved in the program running period, cool, but do not abuse it. 2.2 constructor of class When we instantiate a class, we often want to initialize it at the same time. To do this, we can use the constructor. The constructor is a function without return (Void does not write) and is the same as the class name, which will be automatically executed when the class is instantiated. The use of constructor is like this: #include Using namespace std; Class gras { PUBLIC: INT height; GRASS (INT "; // constructor. Of course, it needs public permissions // Although it has parameters in this program, it doesn't have to be } Grass :: grass (int hotht) { This-> height = height; // For any object's method, this is always a point to this // The pointer of the object. So write this to make the compiler know the Height in the class. } void main () { Grass gras1 (10); // When the normal object is instantiated, the initialization parameters are given. // If the constructor is no parameter, it is not necessary to write "(10)" Grass * grass2; Grass2 = new gras (30); // Pointer to give an initialization parameter at this time Cout < Cout < HEIGHT; } It is worth noting that when you use Grass Grass1 = Grass2; or Grass Grass1 (Grass2); When the object is initialized, the constructor will not be executed, and the execution will be the so-called copy constructor. If you are lazy, don't write it, the system will automatically generate one, and its behavior will be a byte copy graz2 to gras1. This behavior looks normal, but if there is a pointer type member, it has catastrophic consequences. Take a look at the following end code segment: Grass gras1; Grass1.x = "hehe"; // hypothesized X is a char * type member of the Grass class { Grass gras2 = grass1; // At this time, the X of Grass2 will point to the same value of Grass1! } // grass2 is destroyed with its X members // Now Grass1.x has also been innocently lost So, when there is a pointer type member in our class, we must write a copy constructor like this: Grass :: grass (grass & grass1) // Name should be the same as the class, the parameters should be referenced to similar data // If we are written into graz :: grass (grass gras1), it will be obviously falling into the dead cycle, because the compiler needs // Call the copy constructor to generate a replica of the parameter, so we must use the argument { / / Copy the data correctly here } But this is quite troubles, is there a way to ban this meaning is generally not a copy of the copy? Very simple, write a copy constructor that only the declaration is not defined, and declares that it is private permissions, you can prevent the compiler from being self-smart ---- compile if it discovers grass gras1 = gras2; this statement will report an error But don't want to use foo (grass a); such a function, you must use Foo (Grass & A); ...... The constructor is still a type of transition. For example, we define a such constructor: Grass :: grass (int x) { HEIGHT = X; } Now, if we define a GRASS class GG object, you can execute GG = 5 in the future; such a statement, you can also assign the Int type variable to GG, because it actually executed GG = Grass (5) This statement (if we use the method described in Section 2.4 overload = operator, only the overloaded = operator). There is also something called the analyte function, like grass :: ~ grass (), when we delete a pointer to the object, you should call, you should release the pointer members of the class. 2.3 static members The static data member of the class and the normal static variable mean are different. It means that the storage space is not allocated when each class is instantiated, but each object of this class share a storage space, and all objects of the class This storage space can be accessed directly. In fact, it is a variable that is specifically for this class ---- If you declare it as a private permission. Defining a static data member in the class, you only need to add "static" in front of the definition. The static data member of the class can only initialize the class, and if it is not initialized, it is automatically initialized to 0. Reference static data members except the class must always use the class name :: variable name. Static data members can be used to count how many such objects have been created. for instance: #include Using namespace std; Class aa { Private: Int a; PUBLIC: Static int count; // Define a static member of the class AA (int AA = 0) {a = aa; count ;} / / For the method of the class, if it is more simple, you can write this to make the program compact INT GET_A () {RETURN A;} } INT aa :: count = 0; // In-class initialization void main () { COUT << "count =" < < AA X (10), Y (20); COUT << "x.a =" < 2.4 operator overload Operator overload can make the class very intuitive and easy to use. For example, we define a plurality of classes (what, you have not learned the plurality? Do you read a few grades?), Then add, subtract, multiply, divide the operator, you can freely make the plurality of integers. These operations! I can imagine that this will greatly facilitate us. Using the operator overload is simple, we will give an example of a plural class to explain how to use: #include Using namespace std; Class Complex { Private: Double REAL; Double image; PUBLIC: Complex (); // default constructor Complex (Double R, Double I); // The constructor of the way by the way Complex Operator (Complex X); // Calculate A B Complex Operator (); // Calculate A Complex Operator - (int); // Calculate A- COMPLEX Operator = (Double X); what should I do when I assign a Double to a Complex? // The system also automatically generates a complex operator = (Complex); its implementation is a simple copy // So if there is a pointer member in the class, it will be like the default copy constructor? // If we have to rewrite it, pay attention to check what you have given yourself. Void print (); // Output plural } Complex :: Complex () { REAL = 0.0F; Image = 0.0F; } Complex :: Complex (Double R, Double I) { REAL = R; Image = i; } Complex Complex :: Operator (Complex X) { Complex C; C.REAL = Real x.real; C.Image = image x.image; Return C; } Complex complex :: Operator () { Complex C; Real; C.REAL = REAL; C.IMAGE = Image; Return C; } Complex Complex :: Operator - (INT) { Complex C; C.REAL = REAL; C.IMAGE = Image; Real -; Return C; } Complex complex :: Operator = (double x) { Real = x; Return * this; // Return * this in accordance with C practice to achieve chain expressions } Void Complex :: Print () { Cout < << " " < << "i" < } void main () { Complex A (1, 2); Complex B (4, 5); Complex c = a b; Complex D = A; Complex E = B -; // Complex f = 0.234; // This written now does not work, because there is no corresponding copy constructor above // You can try to write one Complex f; f = a = 0.234; // chain expression a.print (); C.Print (); D.print (); E.Print (); F.Print (); } In addition to ".", ". *", "::", ":", other operators (including new, delete) can be overloaded, CIN and COUT are two typical examples. For binocular operators (ie, a? B), such as plus, minus, multiplication, division, etc., can be overloaded: "Complex Operator? (Complex B);", it seems that this method of calling A is the same. For the front single-grade operator (ie), such as "-a", "--a", " A", etc., can be overloaded like this: "Complex Complex :: Operator? ();". For post-type single operators, such as "A-", "A ", so you can overload: "Complex Complex :: Operator? (int);", in which Int can't be omitted in the parameter table. Let's take a look at it: Create a string class and turn , -, =, == and other operators, so that we can operate the string intuitively. 2.5 inheritance It can be inherited the second advantage of the class, which makes the structure of the large program strictly and reduces the repetitive labor of the programmer. What is inherited? For example, the trees and cats are two things, it seems that it does not coherent, but they all have a common attribute and sale, weighing, etc. of mass, volume. So we can't define a base class first, it contains only two things shared attributes and methods, and then derive the two things of trees and cats, so that they inherit all properties of the base class to avoid duplicate definitions? The answer is yes. Since a class can inherit multiple classes at the same time, a class can be inherited by multiple classes at the same time, we can build a complex inheritance relationship, just like this: Figure 2.1 We can see that inheritance is very flexible. To explain that inheritance is very simple, as long as you define this class like this: Class derived class name: derived nature base class name 1, derived nature base class name 2, ..., derived nature base class name N { As with the definition of ordinary classes, this is not necessary to explain the members in the base class. } About the derived nature here, there is such a table for reference: Table 2.1 Access rights in the base class in the base class Access permission Public public public public public public protected protected PRIVATE is not accessible protected public protected protected protected PRIVATE is not accessible Private public private Protected private PRIVATE is not accessible The latter two columns in this table means that when a member of this access authority is set in the base class, it will be equivalent to a member of the derived class that sets any access. Let's take an example: #include Using namespace std; Class Thing // Define the base class { protected: Int mass, Volume; PUBLIC: Thing (int M, int V); INT get_mas (); Void set_mass; } Thing :: Thing (int M, int V) { Mass = m; Volume = v; } Int shut :: get_mass () { Return mass; } Void think: set_mass (int new_mass) { IF (new_mass> = 0) Mass = new_mas; } Class Animal: Public Thing / / Define Acquisition Class { Private: INT Life; PUBLIC: Animal (INT X): Thing (10 x, 7) {Life = x;}; // Need to give a derived class constructor // Measures for initialization base classes // If there are multiple base classes, separate // separate // separately // separately Void set_life (int new_life) {if (new_life> = 0) Life = new_life;}; Int get_life () {return life;}; Void kill () {life = 0; } Animal Cat (50); void main () { Cout < Cout < Cat.set_life (100); // also has its own method Cat.kill (); Cout < } When a certain class has inherited multiple classes and these classes have functions of the same name, we can use this kind of statement to explain which class to use: Child-> Father :: get_life () ;. 2.6 virtual functions and abstract classes The virtual function reflects the third advantage of the class: polymorphism (it seems to be very affectionate). Sometimes, in a program containing a base class and a derived class, we need to define one and base classes in the derived class with the same function name, return type, and parameter table, but the specific content of the function is different. For example, we first define a "plant" class, and then define some of its derived class "pine", "willow", "poplar", etc., and then overload "planting" method in derived class. Because we know that its implementation is different with the tree species. However, when a "plant" pointer points to a "willow" class (this is legitimate), the base class pointer is only the "planting" method of the base class, rather than redefining in the derived class. Methods! The solution to the problem is to define this method as a virtual function in the base class. The definition method of virtual functions is to add keyword "virt" when the base class declares member function. We also give an example to illustrate the method of use of virtual functions: #include Using namespace std; Class Base { PUBLIC: Int a; Virtual int GET_A () {RETURN A; } Class Child: Public Base { PUBLIC: INT GET_A () {RETURN A * a;}; CHILD (INT AA) {a = aa;}; } Child CHild (10); void main () { Base * p; P = & child; Cout < Get_a (); } As can be seen from the operation results, calling the CHILD class GET_A (). You can try to delete virtual to see what the output changes. It is worth noting that the destructor of the base class must be a virtual function, otherwise it will clearly call the sectual destructor of the derived class when you pass the object of the base class delete. In addition, the function of the derived class overload base is no effect, and the compiler will only select which function calls according to the type of pointer. Sometimes we don't need to define objects with base classes, you can define the function of the base class as a pure virtual function, nor does it require the implementation of functions in the base class. At this time, the base class is called an abstract class. Or which plant is planted, because we obviously do not define a "plant" object, it will only choose a corresponding class according to the specific tree, so we can complete the "plant" class. The virtual function is all defined as a pure virtual function. The method of defining a pure virtual function is to remove the correspondence after adding "Virtual" and add "= 0" after the declaration. Just like this: "Virtual INT GET_A () = 0;". 2.7 template Templates are an interesting and useful thing provided by the C language that allows us to quickly define a series of similar classes or functions. Let's take a look at how to use templates to define classes: #include Using namespace std; Template // This is a so-called prefix, ie prefix. <> Template parameters, here // is a class T. With this prefix, the following half statements can take T // Use a name of a class to use Class List { Private: T * a; // a is a pointer to the T type data PUBLIC: Int size; List (INT N); T operator [] (INT i); // list [] return value is T type data ~ List (); } Template List :: List (int N) // Wow! Is this a book ... // It's actually very good, first put the prefix, // The remaining List middle Be a must // Repeat the parameter table { A = new t [n]; // Make A to an array of data for a member for T type For (int i = 0; i a [i] = (t) (i 47); // assign content to A array Size = n; } Template List :: ~ List () { delete [] a; } Template T list :: Operator [] (INT i) // Note List The T is // Type of the return value of this function { RETURN A [i] 1; // and ordinary [] have a little difference? } void main () { List c (10); // Provide parameters to the template, indicating that t is char // We can see charity as a class For (int i = 0; i Cout < } We can define a template with multiple parameters in a similar approach, such as: #include Using namespace std; Template Class List { Private: T * a; PUBLIC: Int size; List (); T Operator [] (INT I); ~ List (); } Template List :: List () { a = new t [u]; For (int i = 0; i SIZE = u; } Template T list :: Operator [] (INT I) { Return a [i] 1; } Template List :: ~ List () { delete [] a; } void main () { List C; // Note that you can only assign a constant to the "actual" parameter of the template // Because the essence of the template is replaced! Just like #define // So you don't have to worry about its efficiency? For (int i = 0; i Cout < } Let's take a look at how to define functions with template: #include Using namespace std; Template T print (t n); // and definitions are similar, return value is T type, parameter // n is also T type Template T Print (T N) { Cout < < Return n; } void main () { Float x = 3.14; Print (x); CHAR Y = 'm'; Cout < } Do you think it is a bit like a function overload? But don't waste time to write almost exactly the same function. Remember the function of the #include defined by the 1.3 section? It is suitable for all data types, but now, we can use the template to achieve exactly the same feature. Note that the compiler implementation template is actually based on how much a bunch of depends on the data type. In fact, the introduction of the template has a significant meaning like the introduction of the initial class, and a new programming ideas come to life: Generic Programming (GP). The core of this programming idea is to make algorithms abstraction so that it can be applied to all data types. The famous STL (Standard Template Library) is the application results of this idea. Interested readers can find some books in this area, and improve their programming levels will be beneficial. 2.8 Optimizer First, remind everyone that the optimization on the statement is better than the huge benefits brought about by the optimization of the algorithm, so I think people who are not familiar with themselves should buy the books and algorithms. Look. In Chapter VII tells several common algorithms, if you are interested, you can look. Let's go to the topic, tell a general optimization skill: (1) Use the inline function. (2) Expand the cycle. For (i = 0; i <100; i ) { DO_STUFF (i); } Can be expanded: For (i = 0; i <100;) { DO_STUFF (I); i ; DO_STUFF (I); i ; DO_STUFF (I); i ; DO_STUFF (I); i ; DO_STUFF (I); i ; } (3) The intensity of the operation is weakened. For example, if there is such a program: INT x = w% 8; INT Y = x * 33; Float z = x / 5; For (i = 0; i <100; i ) { H = 14 * i; Cout < } The above program can be greatly accelerated: INT x = W & 7; INT Y = (x << 5) x; // << More than is priority! Float z = x * 0.2; For (i = h = 0; i <100; i ) { Cout < H = 14; } (4) Character. This method is quite useful. For example, we define a function f (x) to return x * x * x, where x is 0 ~ 1, the accuracy is 0.001, then we can build an array a [1000], a [t * 1000] storage It is the value of the T * T * t, which is calculated in advance, and then call the function to replace it with a lookup array. 2.9 debugging procedure Everyone will make mistakes in programming. After the first operation, the first operation is directly passed, and occasionally one or two are worth a happy thing. It is wrong to change, but many times the hardest is not correct, but finds an error, sometimes the time to write the program is not as long as the error. In order to help you save a little time, let's talk about a little experience in finding a mistake. First of all, I must say that there is a common mistake. The most often occurring: leakage number, multi-segment, leak various parentheses, multi-parentheses, "==" write "=" (above the error looks very Needly, but also it is also easy to commit), array (pointer) crosses (one of the most common errors!), The variable offline, the initial value before the pointer is used, release the pointer to continue using it ... and more. If your program sometimes mistakes, it is sometimes wrong, it is likely to be a pointer problem. One thing to pay attention to is that VC.NET shows the wrong way may not be true! Commonly used to find the wrong way first to confirm what statements you have just changed, then use / * and * / to remove possible statement barriers, if you don't pass the scope. Even if you have a program, you don't think there is any problem or try your barrier. Sometimes it seems that it seems to be the least wrong. There is also a way to find the wrong way to use the value of some variables on the screen, or put the detailed process of running the program into the file, what is the problem. If you use a "console" like Quakeiii, it is cool. Like other compilers, VC.NET provides variable observation (Watch), single-step execution (STEP), and other routine adjustment methods, of course, you first need to set the project to Debug mode. Then set a breakpoint (click on the gray area to set the left side of the broken point, there will be a red circle, the program is running to this, and the debug can be started to start pressing F5. A debug toolbar appears at this time: Figure 2.2 Debug toolbar The meaning of the icon is: executing this statement, stopping this statement, stop debugging, reinterping, displaying the upcoming statement, debugging functions, skipping the function, debug this {}, using hexadecimal display data, Show breakpoints. Everyone will notice that a variable observation window appears in the lower left corner, where it can be very convenient to observe the value and change of variables. We can also open an anti-assembly window, memory observation window, and register observation window, which is very powerful, very cool. The code generated by the compiler is also a good way to understand the truth behind the C language ornate appearance. VC also provides two debug statements to help you debug, the first statement is Assert. Its usage method is Assert (Condition), you can put it in the need, will display a dialog when the condition is not satisfied, explains which program does not satisfy, then you can choose to stop, Continue or ignored. This statement is very useful because the program is executed (not debug in VC ) it can also work. The second statement is OutputDebugString (string to output), which can be compiled under the screen to compile the window to display this string. Using VC.NET development environmental commissioning is a very convenient thing, as long as you have more debugging (this doesn't have to deliberately pursue, because it is inevitable?), You can be more and more proficient. The C language part of this book can be seen here, but it is just a c language of the iceberg, because the C language can be called a profound, and it is still growing. I hope that you should not stop learning and research on C languages in the future, you will constantly have new feelings and discovery. Finally, it is recommended to read a good book: Scott Douglas Meyers Effective C and More Effective C (many of which I have been inserted into the previous text?). Chapter III, accommodating the space of the game Because our chorked game will run under Windows, learning a little Windows programming knowledge is required. Microsoft has created a huge library MFC for convenience of Windows, and encapsulates all aspects of WINDOWS. However, this class is just a standard Windows program that is suitable for writing a panel. It is too cumbersome to the game, so we generally do not use it, you use Windows API (Application Programming Interface Application Programming Interface, In fact, a bunch of Windows provides a function provided by the developer) Write a Windows program. 3.1 Basic Windows Programs The most basic Windows program looks a bit long, and its flowchart is like this: Figure 3.1 But you don't have to worry about Windows programming too complicated. In all Windows programs, there is a need for an initialization process, and this process is very small for any Windows program. You may think that using VB to make a simple program without knocking a line of code, in fact, this is because VB has already jeensed it. #include // Function declaration Bool Initwindow (Hinstance Hinstance, INT NCMDSHOW); LResult Callback WinProc (HWND HWND, UINT MESSAGE, WPARAM WPARAM, LPARAM LPARAM); // Variable description HWND HWND; // Window handle // ******************************************************** ************ // Function: WinMain () // Function: Windows program portfolio function. Create a main window, process the message loop // ******************************************************** ************ Int Pascal WinMain (Hinstance Hinstance, Hinstance Hprevinstance, LPSTR LPCMDLINE, INT NCMDSHOW) { IF (! INITWINDOW (Hinstance, ncmdshow) Return False; // Create a main window // Returns false and exit the program at the same time if you create unsuccessful MSG msg; // Enter the message loop: For (;;) { IF (PEEKMESSAGE (& MSG, NULL, 0, 0, PM_REMOVE)) { IF (msg.message == wm_quit) Break; TranslateMessage (& MSG); DispatchMessage (& MSG); } } Return msg.wparam; } // ******************************************************** ************ // Function: initwindow () // Function: Create a window // ******************************************************** ************ Static Bool InitWindow (Hinstance Hinstance, Int Ncmdshow) { // Define window style: WNDCLASS WC; wc.style = null; Wc.lpfnWndProc = (WndProc) WinProc; wc.cbclsextra = 0; wc.cbWndextra = 0; wc.hinstance = hinstance; wc.hicon = null; wc.hcursor = null; wc.hbrbackground = Createsolidbrush (RGB (100, 0, 0)); // dark red background Wc.lpsz GeneNuname = NULL; wc.lpszclassname = "my_test"; RegisterClass (& WC); // Register window / / Create a window according to the parameters given HWND = CREATEWINDOW ("my_test", "My first program", WS_POPUP | WS_MAXIMIZE, 0, 0, GetSystemMetrics, // This function returns the screen width GetSystemMetrics (SM_CYSCREEN), / / This function returns the highlights of the screen NULL, NULL, HINSTANCE, NULL; IF (! hwnd) Return False; ShowWindow (HWND, NCMDSHOW); // Display Window UpdateWindow (hwnd); // Refresh window Return True; } // ******************************************************** ************ // Function: WinProc () // Features: Processing Window Messages // ******************************************************** ************ Lresult Callback WinProc (HWND HWND, UINT MESSAGE, WPARAM WPARAM, LPARAM LPARAM) { Switch (Message) { Case WM_KeyDown: // Key Message Switch (WPARAM) { Case vk_escape: MessageBox (hwnd, "ESC key is pressed! Determine and exit!", "Keyboard", MB_OK); PostMessage (hwnd, wm_close, 0, 0); // Send a WM_CLOSE message for the window Break; } Return 0; // Returns 0 after processing one message Case WM_Close: // Prepare to exit DestroyWindow (hwnd); // Release window Return 0; Case WM_RBUTTONDOWN: Messagebox (hwnd, "right mouse button!", "Mouse", mb_ok); Return 0; Case WM_DESTROY: // If the window is released ... PostquitMessage (0); // Send a wm_quit message for the window Return 0; } // Call the default message processing process Return DefWindowProc (Hwnd, Message, WPARAM, LPARAM); } After setting up a project according to the 1.1 section, enter the program, press CTRL F5 to execute, there will be a dark red "window". Then you can try to press the right mouse button or the ESC button to see the effect, just like Figure 3. 2. how about it? VB is going to do the same effect, I am afraid it is a bit trouble, this is also a little benefit from the code code. Figure 3.2 3.2 WinMain function 3.2.1 Introduction The winmain () function is basically the same role with the main () function of the DOS program, but a little difference is that the WinMain () function must have four systems pass to it. The prototype of the WinMain () function is as follows: INT Pascal Winmain (Hinstance Hinstance, Hinstance Hprevinstance, LPSTR LPCMDLINE, INT NCMDSHOW) The first parameter hinstance is a handle that identifies the application. But what is the handle? It is actually a pointer to the memory area occupied by the program, uniquely represents the app, Windows uses it to manage various objects in memory. Of course, it is very important. It is necessary to use it as a parameter during the process of the initialization program main window. The second parameter is HPREVINSTANCE, give it null, this parameter is just to maintain compatibility with 16-bit Windows applications. The third parameter is lpcmdline, which is a pointer to the application command line parameter string. For example, we run "Test Hello", the string of this parameter points to "Hello". The last parameter is ncmdshow, an integer used to specify the window display mode. The type of window display mode will be explained below. 3.2.2 Registration window class A program can have many windows, but only one is the main window, it is the only one that is unique to the application. You can usually populate a window WNDClass before creating a window, and register the window class in REGISTERCLASS (). Each window has some basic properties, such as window title bar text, window size, background color, window message processing function (later this function), and so on. The process of registration is to tell the system, then call the CREATEWINDOW () function to create a window. The members of WNDCLASS are listed below: Uint style; // window style WndProc LPfnWndProc; // Pointer for Window Message Processing Functions INT CBCLSEXTRA; / / The number of additional bytes after assigning the window structure INT CBWndextra; // The number of additional bytes after assigning the window instance Handle Hinstance; // The handle of the application corresponding to the window Hicon Hicon; // window icon Hcursor HCURSOR; / / Window mouse Hbrush Hbrbackground; // Window background LPCTSTR LPSZMENUNAME; // The menu resource name of the window LPCTSTR LPSZCLASSNAME; // Name of the window class The first member style of Wndclass represents the style of the window class, which is often combined by some basic style "or" operations (operators | "). The following table lists some common basic window style: chart 3.1 Style meaning CS_HREDRAW If the window width changes, redraw over the entire window CS_VREDRAW If the window height changes, redraw the entire window CS_DBLCLKS can feel the double-click message in the window CS_NOCLOSE Disables the "Close" command in the system menu CS_SAVEBITS saves the screen image portion covered by the window as a bitmap. When the window is moved, Windows uses the saved bitmap to rebuild the screen image The second member is lpfnwndproc, which gives the function name of the message processing function. It is necessary to force the mandatory type conversion, convert it to WNDPROC type. . The next CBCLSEXTRA and WC.CBWndextra can generally be set to zero. Then the Hinstance member gives it a handle of the application corresponding to the window, indicating that the window is associated with this application. The following HiCon is to let us specify an icon to this window, which is not set. The mouse is not set, because the mouse in the game is drawn on the screen. HBRBackground member used to define the background color of the window. Here is CreateSolidbrush (RGB (100, 0, 0)), ie dark red. For the CreateSolidbrush function, please refer to Section 4.10. The value of the LPSzMenuname member we give NULL, indicating that the window does not have a menu. The last member of WndClass is a unique name to let us give this window class because there are many window classes in the Windows operating system. Typically, we can use the program name to name the name of this window class. This name will be used when calling the CREATEWINDOW () function. After filling WNDCLASS, we need to call the registerclass () function to register; the function returns a non-0 value, indicating that this window class has been registered in the system. If it fails, it returns 0. 3.2.3 Creating a window When the window class is registered, we can create a window, which is done by calling the CreateWindow () function. The general properties of the window have been predefined in advance, and in the parameters in the createWindow (), the window can be further specified. Let's take an example to illustrate the usage of CreatWindow (): HWND = CREATEWINDOW "Simple_Program", // Create the name of the window class used to use "A Simple Windows Program", // Window Title WS_OVERLAPPEDWINDOW, // Window style, defined as ordinary 100, // X coordinate of the window position 100, // Y coordinate of the window position 400, // Width 300, // The height of the window Null, // Parent Window Handle Null, // Menu handle Hinstance, // application handle NULL); / / generally null The first parameter is the name of the window class used to create the window, note that this name should be consistent with the name of the window class registered earlier. The third parameter is the style of the created window, the following table lists the common window style: Table 3.2 Style meaning WS_OVERLAPPEDWINDOW Creates a laminated window, a border, title bar, system menu, and maximum minimum button, is a collection of the following style: WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX, WS_MAXIMIZEBOX WS_POPUPWindow Creates a pop-up window, which is a collection of styles: WS_Border, WS_POPUP, WS_SYSMENU. You must add WS_CAPTION to in order to make the window menu can be seen. WS_OVERLAPPED & WS_TILED Creates a laminated window with a title bar and a border. WS_POPUP This window is a pop-up window that cannot be used simultaneously with WS_CHILD. The WS_BORDER window has a single line border. The WS_CAPTION window has a title bar. WS_CHILD This window is the child window and cannot be used simultaneously with WS_POPUP. WS_DISABLED This window is invalid, that is, no response to user operations. The WS_HSCROLL / WS_VSCROLL window has a horizontal scroll bar / vertical scroll bar. The ws_maximize / ws_minimize window initializes the maximum / minimize. WS_MAXIMIZEBOX / WS_MINIMIZEBOX window has the maximum button / minimization button WS_SIZEBOX & WS_THICKFRAME border can make a size control window WS_SYSMENU Creates a window with a system menu, must be used at the same time as the WS_CAPTION style WS_TILED creates a stacked window with title bar WS_Visible window is visible In DirectX Programming, we generally use WS_POPUP | WS_MAXIMIZE, and the window created with this flag does not have the title bar and the system menu and the window is maximized, and the need for DirectX programming can be fully met. If the window is created, createWindow () returns the handle of the new window, otherwise returns NULL. 3.2.4 Display and Update Window After the window is created, it will not be displayed on the screen. To really display the window on the screen, you have to use the showWindow () function, its prototype is as follows: Bool ShowWindow (HWND HWND, INT NCMDSHOW); The parameter hWnd is the handle of the window to display. NCMDSHOW is a window display method, which is usually the value of NCMDSHOW to which the WinMain () function is obtained. Common window display methods are: Table 3.3 Method meaning SW_HIDE hidden window SW_MINIMIZE minimization window SW_RESTORE Restores and activates the window SW_SHOW display and activate the window SW_SHOWMAXIMize maximizes and activates the window SW_SHOWMINIMIZED minimizes and activates the window The showWindow () function is not high, and the window does not display immediately when the system is busy performing other tasks. So we need to call UpdateWindow (HWND) after using the showWindow () function to ensure that the window is displayed immediately. 3.2.5 Message Cycle In the Winmain () function, call the initwindow () function successfully created the application main window, start the message loop, the code is as follows: For (;;) { IF (PEEKMESSAGE (& MSG, NULL, 0, 0, PM_REMOVE)) { IF (msg.message == wm_quit) Break; TranslateMessage (& MSG); DispatchMessage (& MSG); } } Windows applications can receive various forms of information, including the action of the keyboard, the mouse, the message, the message sent by other applications, and the like. The Windows system will automatically put these messages into the application's message queue. The peekMessage () function is to take these messages one by one by the first-first principle, put it in a MSG structure in the message queue of the application. If there is no message in the queue, the peekMessage () function will return immediately. If there is a message in the queue, it will remove one after another. The MSG structure contains a complete information of a Windows message, which consists of several parts: HWND HWND; // Receive the window handle of the message Uint message; // main message value WPARAM WPARAM; // Secondary Message Value 1, its specific meaning depends on the main message value LPARAM LPARAM; // Sub-message value 2, its specific meaning depends on the main message value DWORD TIME; // message is delivered Point pt; // mouse position The main message in this structure indicates the type of message, for example, a keyboard message or a mouse message. The meaning of the sub-message relies on the main message value. For example, if the main message is a keyboard message, then which specific key is stored in the keyboard; if the primary message is a mouse message, then Loword (LPARAM) and HiWord (LPARAM) For the X and Y coordinates of the mouse position; if the main message is WM_ACTIVATE, WPARAM represents whether the program is active. By the way, after defining a Point type variable CURPOS, use GetCursorpos (& Curpos) in any location of the program to store the mouse coordinate in Curpos.x and Curpos.y. The prototype of the PEEKMESSAGE () function is as follows: Bool PeekMessage LPMSG lpmsg, // Pointer to a MSG structure, used to save your message HWND hWnd, / / Specify which window of the window will be obtained UINT WMSGFILTERMIN, / / Specify the minimum value of the obtained main message value UINT WMSGFILTERMAX, / / Specify the maximum value of the obtained main message value UINT WREMOVEMSG / / Whether to remove the message after the message ); The first parameter of PeekMessage () is explained. The second parameter is used to specify which window of the message queue from which the message queue is acquired, and the message of the other window will be filtered out. If this parameter is null, PeekMessage () gets a message from the message queue of all windows of the application. The third and fourth parameters are used to filter the main message value in the MSG structure, the main message value outside WMSGFILTERMIN and WMSGFILTERMAX will be filtered out. If both parameters are 0, it means to receive all messages. The fifth parameter is used to set whether the message is removed from the queue after dividing the message, which is generally set to PM_Remove to remove. The effect of the translateMessage () function is to convert the virtual key message to the character message to meet the needs of the keyboard input. The DISPATCHMESSAGE () function is completed by sending the current message to the corresponding window. Turning on the message loop is actually a simple step, almost all programs are in Test's method. We don't have to go through the role of these functions, just a simple photo. In addition, the message loop opening method introduced here is better than the method of using getMessage () than some books, because getMessage () If the message will wait, the result will cost a lot of valuable time, make the game Can't refresh in time. 3.3 Message Processing Function The message handler is also called a window process. In this function, different messages will be allocated to different handles by the Switch statement. The prototype of Windows message processing function is such a definition: LResult Callback WindowProc HWND HWND, // Receive the handle of the message window UINT UMSG, // main message value WPARAM WPARAM, // Deep Message Value 1 LPARAM LPARAM // Sub-message Value 2 ); The message processing function must be defined in this style of the above, and of course the function name can be taken casually. The WinProc () function in TEST is a typical message processing function. In this function, 3 messages are clearly dealt with, which are wm_keydown, wm_rbuttondown (right-click), WM_Close, WM_DESE, WM_DESTROY (Destruction Window). It is worth noting that the application sent to the window is far more than the above, like WM_SIZE, WM_MINIMIZE, WM_CREATE, WM_MOVE, etc. There are dozens of frequently used frequently. In the appendix, you can find a list of frequently available messages. To mitigate the burden of programming, Windows provides a defWindowProc () function to handle these most common messages. After calling this function, these messages will be processed in the system default manner. Therefore, in the message processing function, only those messages necessary for the especially responding are required, and the remaining messages can be handed over to the DEFWINDOWPROC () function. 3.4 Common Windows Functions 3.4.1 Display dialog The Messagebox function can be used to display the dialog, its original shape is: INT MessageBox (HWND HWndParent, LPCSTR LPSZTEXT, LPCSTR LPSZTILE, UINT FUSTYLE); The four parameters are: window handles, text content, title, style. The common style is: MB_OK, MB_OKCANCANCEL, MB_RETRYCANCANCANCANCEL, MB_RETRYCANCAN, MB_YESNO, MB_YESNOCANCEL, representing which buttons in the Represents dialog box. Common return values have IDCANCEL, IDNO, IDOK, IDRETRY, iDYES, represent which button is pressed. 3.4.2 Timer The timer can make the program execute a function every other time. Usage is as follows: SetTimer (HWND HWND, UINT ID, UINT ELAPSE, TIMERPROC TIMERFUNC); The four parameters are in turn, the timer identity (the identity of each timer in the same program should be different, generally row from 1, 2, 3 ...), every milliseconds (thousands of seconds) Perform a program to perform the process. This process to be executed should be defined like this: Void Callback MyTimer (HWND HWND, UINT UMSG, UINT Idevent, DWORD DWTIME); These specified parameters are not used, we do our own things in the process, do not need these parameters to give us. Note: The timer is not high, and when the processor is very busy, the programs we need to perform regularly cannot be performed on time; no matter how small the timer's ELAPSE is set, it actually only 55ms; The Windows function can't be used in Timerfunc, and don't do some time-time things in Timerfunc. 3.4.3 Getting time We often need to get current exact times in the program to complete the test speed. At this time we can use gettickcount () because the function can return to Windows how many milliseconds have been run. However, sometimes we need to get more accurate time, then this method can be used: __INT64 TIME2, FREQ; // Time, Timer Frequency Double Time; // Time in seconds QueryPerformanceCounter (Large_integer *) & Time2); // Get the time starting QueryperFormanceFrequency ((Large_integer *) & freq); // Get timer frequency Time = (time) / (double) / (Double) freq; // Play the time to in seconds unit 3.4.4 play sound We can use MCI to easily implement sounds such as MIDI and WAV in the program. Using it needs pre-declaration, we need to be in the file head #include And add "WinMM.LIB" in the project Let's take a look at the process of playing MIDI. First we have to open the device: MCI_open_parms openparms; OpenPARMS.LPSTRDEVICEPE = (Lpcstr) MCI_DEVTYPE_SEQUENCER; / / is the MIDI type file OpenPARMS.LPSTRELEMENTNAME = (LPCSTR) filename; // file name OpenPARMS.WDEVICEID = 0; // Opened device identifier, then need to use McISendCommand (NULL, MCI_Open, MCI_Wait | MCI_Open_Type | MCI_open_type_id | mci_open_element, (DWORD) & OpenParms); // Open the device Then you can play MIDI: MCI_Play_Parms Playparms; PlayParms.dwfrom = 0; // What time is played, in milliseconds MCISENDCOMMAND (DeviceID, MCI_Play, // DeviceID needs to be equal to the above device ID MCI_FROM, (DWORD) & PlayParms); // Play MIDI Stop play: MCISENDCOMMAND (DeviceID, MCI_Stop, Null, NULL); Finally, the device is turned off: MCISENDCOMMAND (DeviceID, MCI_Close, Null, NULL); Opening the WAV file is almost identical to the way to open the MIDI file, just need to change MCI_DEVTYPE_SEQUENCER to MCI_DEVTYPE_WAVEFORM_AUDIO. Chapter 4 draws the brush of the game After reading the previous chapter, we have gradually mastered the method of Windows programming. This means we have made a solid foundation for DirectDraw programming. DirectDraw is an important part of DirectX, which is like a brush, mainly responsible for displaying various images on the screen, very important to the game in the Windows environment. Let us now enter the exciting DirectDraw section. 4.1 Initializing DirectDraw In order to tell the compiler, we need to use DirectDraw, we have to be in the program file #include And join the "DDRAW.LIB" and "DXGUID.LIB" (if you don't add, see section 1.1). Remember, the DirectDraw program can be used normally after completing these work. 4.1.1 Introduction Let's take a look at a common DirectDraw initialization function: LPDIRECTDRAW7 LPDD; Pointer for // DirectDraw object LPDIRECTDRAWSURFACE7 LPDDSPRIMARY; / / DirectDraw Main page pointer LPDIRECTDRAWSURFACE7 LPDDSBUFFER; // DirectDraw Background Cache Pointer LPDIRECTDRAWSURFACE7 LPDDSBACK; / / Pointer in the background diagram Bool initddraw () { DDSurfaceDesc2 DDSD; // DirectDraw page description IF (DirectDrawCreateex (NULL, (VOID **) & LPDD, IID_IDIRECTDRAW7, NULL)! = DD_OK) Return False; // Create a DirectDRAW object / / This is an error detection method using the method of IF (xxx! = DD_OK), which is the most common method. IF (LPDD-> SetCooperativeEvelevel (HWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN)! = DD_OK) Return false; // Set the DirectDraw control level IF (LPDD-> SetDisplayMode (640, 480, 32, 0, DDSDM_Standardvgamode)! = DD_OK) Return False; // Set the display mode // Start creating the home page, first empty page description MEMSET (& DDSD, 0, SIZEOF (DDSurfaceDesc2); // Fill page description Ddsd.dwsize = sizeof (ddsd); DDSD.DWFLAGS = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; // Have a background cache DDSD.DDSCAPS.DWCAPS = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX; DDSD.DWBACKBuffercount = 1; // a background cache IF (LPDD-> CreateSurface (& DDSD, & LPDDSPRIMARY, NULL)! = DD_OK) Return False; // Create the main page DDSD.DDSCAPS.DWCAPS = DDSCAPS_BACKBUFFER; // This is a background cache IF (DD_OK! = LPDDSPRIMARY-> GetAttachedSurface (& DDSD.DDSCAPS, & LPDDSBUFFER)) Return False; // Create a background cache Ddsd.dwsize = sizeof (ddsd); DDSD.DWFLAGS = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; DDSD.DDSCAPS.DWCAPS = DDSCAPS_OFFSCREENPLAIN; // This is off the screen DDSD.DWHEIGHT = 480; // High DDSD.dwwidth = 640; // Wide IF (DD_OK! = LPDD-> CreateSurface (& DDSD, & LPDDSBACK, NULL) Return false; // Create a page on the background diagram // If there is any other page, you can continue to create here. Return True; } We can see that the pointer to the DirectDraw object and the DirectDraw page (also known as the DirectDraw surface) object is first defined. LPDIRECTDRAW7 and LPDIRECTDRAWSURFACE7 (7 are version numbers) are predefined in DDRAW.H header files, pointing to IDirectDraw7 and iDirectdrawSurface7 types (previously added LP representative long point), from behind we use "-> "Not". "You can also see this. DD is the abbreviation of DirectDraw, and DDS is the abbreviation of DirectDrawSurface, so habits we are using the variables from LPDD and LPDDSxxx. Everyone needs to pay attention to: Although the DirectX SDK comes with VC.NET comes 8.1, because Microsoft stops from DirectX 8.0 to update DirectDraw, DirectDraw is currently 7.0.4.1.2 DirectDRAW object If you want to use DirectDraw, you must create a DirectDraw object, which is the core of the DirectDraw interface. The DirectDrawCreateex () function can create a DirectDraw object, and the DirectDrawCreateEx () function is defined in DDRAW.H, which is as follows: HRESULT WINAPI DirectDrawcreateex Guid Far * LPGUID, LPVOID * LPLPDD, REFIID IID, IUNKNOWN FAR * PUNKOUTER ); The first parameter is LPGUID: points to the global unique marker (Global Unique Identify) of the DirectDRAW interface. Here, we give it null, indicating that we will use the current DirectDRAW interface. The second parameter is LPLPDD: This parameter is the address used to accept the initial DirectDRAW object. Here, we give it to convert to a VOID ** type & lpdd with a mandatory type. This function can change the pointer to the pointer. The third parameter is IID: Give it IID_IDIRECTDRAW7, indicating that we have to create an IDirectdraw7 object. The fourth parameter is PUNKOUTER: Currently a NULL. The return value of all DirectDraw functions is the HRESULT type, which is a 32-bit value. The function call successfully used "DD_OK" to indicate that all the error value flags are "DDERR", such as: DDERR_DIRECTDRAWALREADYCREATED Dderr_OutofMemory These error values are available in the appendix. We generally use "IF (void, (void **) & lpdd, iid_idirectddraw7, null! = Dd_ok) Return False;" to create a DirectDraw object, which will exit function when creating unsuccessful, returning false. 4.1.3 Set control level and display mode After the DirectDrawCreate function calls successfully, LPDD has point to a DirectDraw object, which is the highest level of leadership of the entire DirectDraw interface, and later steps are under its control. We use iDirectDraw7 :: setCoopeRATIVELevel () to set the DirectDraw program to the system's control level. Its prototype is as follows: HRESULT SETCOOPERATIVELEVELEVELEVELEVELEVELEVEL (HWND HWND, DWORD DWFLAGS) The first parameter is the window handle, we give it hWnd, allow the DirectDRAW object to contact the main window. The second parameter is the control level flag. Here is DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN, indicating that we expect DirectDraw to work in exclusive and full-screen. The control level describes how DirectDRAW is working with the display device and system. The DirectDraw control level is typically used to determine the application is running in full screen mode (must be used simultaneously with exclusive mode) or run in window mode. But the control level of DirectDraw can also set the following: (1) Allow Press CTRL Alt DEL to restart (only for exclusive mode, which is DDSCL_Alowreboot). (2) Do not allow the DirectDRAW application to minimize or restore (DDSCL_NOWIDOWCHANGES). Ordinary Control Level (DDSCL_NORMAL) indicates that our DirectDraw application will run in the form of a window. Under this control level, we will not change the display resolution, or make a change operation (this is an important operation, introduced in the 4.2 session). In addition, we cannot call functions that will have a fierce reaction to the memory, such as the LOCK () you want to talk about by the fifth chapter. When the application is a full screen and exclusive control level, we can fully utilize hardware resources. At this time, other applications can still create a page, using DirectDraw or GDI functions, just unable to change the display mode. Next We use iDirectdraw7 :: setDisplayMode () to set the display mode, its original shape is: HRESULT SETDISPLAYMODE DWORD DWWIDTH, DWORD DWHEIGHT, DWORD DWBPP, DWORD DWREFRESHRATE, DWORD DWFLAGS ); DWWIDTH AND DWHEIGHT is used to set the width and height of the display mode. DWBPP is used to set the number of color bits of the display mode. DWREFRESHRATE Sets the refresh rate of the screen, 0 is used by default. DWFlags now the only valid value is DDSDM_Standardvgamode. 4.1.4 Creating a page The next step is to create a DirectDrawSurface object. The DirectDrawSurface object represents a page. You can imagine the page as a canvas that can be depicted by DirectDraw. The page can have many expressions, which can be either visible, called the main page; can also be an invisible page for switching, referred to as a back buffer, after changing the page, It becomes visible (change the page in Section 4.2); there is an unacceptable, called off-screen page, which is to store images. Where the most important page is the main page, each DirectDRAW application must create at least one main page, which generally said that it represents our screen. Before you create a page, you first need to fill a DDSurfaceDesc2 structure, which is the abbreviation of DirectDraw Surface Description, meaning the page description of DirectDraw. Its structure is very large, and it can only make a simplest introduction. It should be noted that it must be emptied before filling this structure! Below is a typical home page page description: Ddsd.dwsize = sizeof (ddsd); / / to the size of the DWSIZE page DDSD.DWFLAGS = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; // Have a background cache DDSD.DDSCAPS.DWCAPS = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX; / / For the main page, there is background cache, change the chain DDSD.DWBACKBuffercount = 1; // a background cache Take a look at the page description of a normal surface: Ddsd.dwsize = sizeof (ddsd); DDSD.DWFLAGS = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; / / High, wide by us to specify DDSD.DDSCAPS.DWCAPS = DDSCAPS_OFFSCREENPLAIN; // This is off the screen DDSD.DWHEIGHT = 480; // High DDSD.dwwidth = 640; // Wide When the page describes the fill, pass it to the iDirectdraw7 :: createSurface () method to create a page. The original shape of CreateSurface () is: HRESULT CREATESURFACE LPDDSURFACEDESC2 LPDDSURFACEDESC, LPDirectdrawSurface Far * LPLPDDSURFACE, IUNKNOWN FAR * PUNKOUTER ); The first parameter of the createSurface () function is the address of the DDSurfaceDesc2 structure that is filled with page information, here is & DDSD; the second parameter is the address of the receiving home page pointer, here is & lpddsprimary; the third parameter must now be NULL, reserved for this function. If the function call is successful, LPDDSPrimary will become a legitimate homepage object. Since the working mode that has already been set in front is exclusive and full screen, at this time, it is actually our entire display screen. The graphic drawn on the main page will immediately be reflected on our display screen. The DirectDraw initialization function finally creates an off-screen page, if we want to create more pages, because the page description has been filled, you only need to set the height and width to create the page like this: DDSD.DWHEIGHT = XXX; Ddsd.dwwidth = xxx; IF (DD_OK! = LPDD-> CreateSurface (& DDSD, & LPDDSABC, NULL) Return False; 4.2 Backstage Cache and Page Figure 4.1 Backstage cache and change pages are critical to form no flashing animation. To give an example, you want to display an object to exercise on a picture, we need to draw objects in a time, the background of the object to be covered by the object is good, and finally painting the object in the new position at C. . But these operations take a certain time. If we change the page directly, then the user will see no object on the screen, but there is an object on the picture on the A and C, and the user will feel some flashing. How to solve this problem? You can help us with this change in DirectDraw. First, we have to set a chain structure, which consists of a set of DirectDraw pages, each page can be turned from the page to the display screen. The page currently just in the display screen is called the main page. Wait to change the page to the screen called the background cache. The application performs drawing operations on the background cache, and then makes this page into the main page. The original background cache is displayed on the screen, and the original home page has become a background cache, in general, we only need to change Background cache content. So, we have changed the pages after completing B, C and then change the page. We can create a simple double-chain structure (a home page and a background cache), or create a faster multi-switchable page chain structure. Generally, we only need to use a double-bundled chain structure as an example of Section 4.2.1. The function used by the page is iDirectdrawsurface7 :: flip (). Its original shape is: HRESULT FLIP (LPDirectdrawSurface LPDDSurface, DWORD DWFLAGS); The following describes its parameters: (1) lpddsurface Change the address of the IDirectDrawSurface7 interface of another page in the page chain, which represents the target page of the page. This page must be a member of the change page chain. The default value of this parameter is NULL. In this case, DirectDraw is switched from the front and reapperstric relationships from the chain. (2) dwflags Change the logo option of the page, often use ddflip_wait, similar to DDBLTFast_Wait in BLTFAS. Generally, let's change the page: LPDDSPRIMARY-> FLIP (NULL, DDFLIP_WAIT); 4.3 Turn into the image After initializing DirectDraw, it is very simple to transfer bitmap (* .bmp) to the page. We only need to use DDRELOADBITMAP (page, "image. BMP") to be transferred. image. This function is in ddutil.cpp, you need to copy it and ddutil.h into your program, add your project and start in the beginning of the main program #include "ddutil.h" (in chapter iv.zip You can find me modified to adapt to DIRECTDRAW7 DDUTIL.H and DDUTIL.CPP). Note that the DDRELOADBITMAP () function automatically zooms images to accommodate the size of the page. 4.4 Loss and Recovery of Pages When a DirectDraw program is minimized, it lost control of the page, if our program does not know, "DDERR_SURFACELOST" errors occur when we continue to change the page. And when we returned to the DirectDraw program, Windows will not help us recover the page if we don't recover your page users, you will see the black screen. In order to avoid this, we can write a function of recovery page: Void RestoreSurface () { LPDD-> RESTOREALLSURFCES (); // Restore all pages RELOADBITMAP (); // ourselves, re-painted page content } It is worth noting that Windows does not help us resume the actual content of the page, so we want to call your own calligraphics like the above programs. When should I call RESTORESURFACE ()? Is it a statement that changes the page to test whether there is a DDERR_SURFACELOST error? In fact, it doesn't matter. The engine of the general game is frequently refreshed. It is necessary to refresh for dozens of times per second, and each refresh must call the flip () mentioned above. So we can write a flipsurface () and then call it to change the page: Void flipsurface () { HRESULT DDRVAL; DDRVAL = LPDDSPRIMARY-> FLIP (NULL, DDFLIP_WAIT); IF (DDRVAL == DDERR_SURFACELOST) RESTORESURFACE (); } 4.5 transparent color Transparent color (also known as key colors) is critical to the movement of irregular objects. The so-called transparent color refers to the color of the area that is not transmitted in the image transfer. For example, let's draw a person on a pure green background, tune this picture into a page, and set the pure green to this page. In the future, when we perform image transfer, simply specify a rectangle including the character, DirectDraw will only pass the irregular characters to the new page without transferring the pure green background. Of course, the characters itself cannot contain pure green, otherwise it will not be transmitted completely. One page can only have a transparent color at the same time, setting transparent color method is DDSETCOLORKEY (page name, RGB (red, green, blue)); here's "red" "green" "blue" is usually we are in The R / G / B value seen in many image processing software. For example, DDSETCOLORKEY (LPDDSMAP, RGB (255, 0, 255)) ;. Because this function is also DDUTIL, you have to confirm DDUTIL.CPP in the project and #include "ddutil.h". Note that this function is not very fast, because it also converts your color with a skill to ensure that transparent colors can be worked under any color bit. In fact, if you use 32-bit color, we can set the color key directly to you more quickly: DDCOLORKEY DDCK; DDCK.DWCOLORSPACLOWVALUE = RGB (x, x, x); DDCK.DWCOLORSPACEHIGHVALUE = DDCK.DWCOLORSPA PartLOWVALUE; LPDDSXXX-> SetColorKey (DDCKEY_SRCBLT, & DDCK); 4.6 image transfer Making a game with DirectDraw, the most commonly used function is the image transfer (also known as a block transfer) function. Its role is to transmit an image within the specified rectangular range between the respective pages, and can perform various processes simultaneously. Image transfer can be performed using the idirectdrawsurface7 :: blt () and idirectdrawsurface7 :: bltfast () functions. The BLT () function function is very powerful and can scale, rotate, mirror icons, etc. However, usually we can use simple but enough BLTFast (). Its original shape is: HRESULT BLTFAST DWORD DWX, DWORD DWY, LPDIRECTDRAWSURFACE LPDDSRCSURFACE, LPRECT LPSRCRECT, DWORD DWTRANS ); Here, these parameters will be described one by one: (1) DWX and DWY An image will be transferred to the target page. (2) LPDDSRCSURFACE The source page of the image transfer operation. The target page is a page that calls this method. (3) LPSRCRECT An address of a Rect (Rectangle, That is, a rectangle) structure, indicating the area that will be transmitted on the source page. If this parameter is NULL, the entire source page will be used. The Rect structure is very common in DirectDraw, it is best to define a RECT type global variable, such as RECT, and write a function like this: Void MakeRect (int LEFT, INT TOP, INT RIGHT, INT BOTTOM) { Rect.bottom = Bottom; Rect.Left = Left; Rect.right = Right; Rect.top = TOP; } When used, the ip and y coordinates of the left upper left corner of the rectangle are given to the left upper left corner of the rectangle, respectively, and the X and Y coordinates of the lower right corner. (4) DWTRANS Specify the transfer type. There are several following: DDBLTFAST_NOCOLORKEY Specifies a normal replication, without a transparent component. DDBLTFAST_SRCCOLORKEY Specifies to perform an image transfer with transparent color, use the transparent color of the source page. DDBLTFAST_WAIT If the image transmitter is busy, returning until the image transmitter is ready and transferred. This parameter is usually used. These types are long and often used, preferably define two global variables: DWORD SRCKEY = DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAITDWORD NOKEY = DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT Some examples. Use: LPDDSBuffer -> Bltfast (0, 0, LPDDSBACK, NULL, NOKEY); If we want to put the LPDDSSPIRIT (20, 30) to (50, 60) on LPDDSBuffer, and the upper left corner is in LPDDSBuffer (400, 300), use transparencies, use: MakeRect (20, 30, 50, 60); LPDDSBuffer -> Bltfast (400, 300, LPDDSSPIRIT, & Rect, srcKey); Note that the DirectDraw's BLT function is only a point after the source rectangle or the image transmitted to the target page, such as the MakeRect (100, 200, 500, 400), and it will not be Blt! The best way to solve problems is to write a new BLT, tailoring (some books) use clipper to cut, speed than this method). Below you will give this new BLT content (predetermined some of the number of stores with high levels, widths, etc., which is true): Void Myblt (int X, int y, int src_id, int des_id, dword method) { INT RL, RT, TX1, TX2, TY1, TY2, TL, TT; Rect Rect2 = Rect; // Preserve the content of the original RECT RL = Rect.Left; Rt = Rect.top; IF (Rect.Left> SW [SRC_ID]) // SW stores width Goto noblt; // does not perform image transfer IF (Rect.top> SH [SRC_ID]) // SH storage page height Goto Noblt; IF (Rect.Right <0) Goto Noblt; IF (Rect.Bottom <0) Goto Noblt; IF (Rect.Left <0) Rect.Left = 0; IF (Rect.Top <0) RECT.TOP = 0; IF (Rect.right> SW [SRC_ID]) Rect.right = SW [SRC_ID]; IF (Rect.Bottom> SH [SRC_ID]) Rect.bottom = SH [SRC_ID]; TX1 = x Rect.Left-rl; TY1 = Y Rect.top-rt; TX2 = x Rect.right-rl; TY2 = Y Rect.bottom-rt; IF (TX2 <0) Goto Noblt; IF (Ty2 <0) Goto Noblt; IF (TX1> SW [DEST_ID]) Goto Noblt; IF (Ty1> SH [DEST_ID]) Goto Noblt; TL = TX1; Tt = TY1; IF (TX1 <0) TX1 = 0; IF (Ty1 <0) TY1 = 0; IF (TX2> SW [DEST_ID]) TX2 = SW [DEST_ID]; IF (Ty2> SH [DEST_ID]) TY2 = SH [DEST_ID]; RL = Rect.Left; Rt = Rect.top; Rect.Left = TX1-TL RL; Rect.top = TY1-TT RT; Rect.right = TX2-TL RL; Rect.bottom = TY2-TT RT; DDS [DEST_ID] -> Bltfast (TX1, TY1, DDS [SRC_ID], & Rect, Method; // DDS is an array of store pointers Noblt: Rect = Rect2; // Restore the original RECT } 4.7 Program Example See chapter iv.zip 4.8 Image Zoom Image scaling is a common effect, powerful function IDirectdrawsurface7 :: blt () provides this feature. The speed of execution is also unfortunately the effect is not very good, just like the brush in Windows zoom the image. The original shape of the BLT function is like this: HRESULT BLT LPRECT LPDESTRECT, LPDIRECTDRAWSURFACE4 LPDDSRCSURFACE, LPRECT LPSRCRECT, DWORD DWFLAGS, LPDDBLTFX LPDDDBLTFX ); LPDDSRCSURFACE is a pointer of the source page, LPDestRect and LPSRCRECT are the rectangular pointers of the target and source pages, respectively, which will automatically scale. As for dwflags, according to transparent colors, DDBLT_KEYDEST | DDBLT_WAIT, DDBLT_KEYSRC | DDBLT_WAIT or DDBLT_WAIT. The last parameter LPDDBLTFX indicated the effects to use, but unfortunately there is no valuable effect (except for the 5.1) to tell the description of the NULL. 4.9 Release DirectDraw object A complete DirectDRAW program also needs to release all DirectDraw objects in the final release. In order to facilitate this process, we can define a few macros: #define Safe_Delete (p) {if (p) {delete (p); (p) = null;}} #define safe_delete_Array (p) {if (p) {delete [] (p); (p) = null;}} #define safe_release (p) {if (p) {(p) -> Release (); (p) = null;}} Then you can release the DirectDraw object as a program below: void freeddraw () { SAFE_RELEASE (LPDDSPRIMARY); / / Release the main page object // If there are other pages, just like LPDDSPRIMARY SAFE_RELEASE (LPDD); // Release DirectDRAW object } Chapter 5 Rich Picture Tips In the previous chapter, we have learned a lot of DirectDraw's basics. But in order to make a real playable game, we will continue to learn something. Section 4 of this chapter is a very simple game example, you can refer to the reference. 5.1 Filling Color To fill some color within the specified range (generally the transparent color of the population page to achieve the purpose of the clear), you can use the IDirectDrawSurface7 :: blt () function. Usage is as follows: MakeRect (x, x, x, x); // Specify DDBLTFX DDBLTFX; DDBLTFX.DWSIZE = SizeOf (DDBLTFX); DDBLTFX.DWFILLCOLOR = RGB (x, x, x); // to populate color LPDDS-> BLT (& Rect, NULL, & Rect, DDBLT_WAIT | DDBLT_COLORFILL, & DDBLTFX); here is an important issue here, that is, the so-called "555" and "565". We often use 16-bit colors, but 16 except 3 is not integer, then do you need to use how many bits of red, green, blue? Some graphics cards are three colors of 5, namely 0rrrrgggggbbbbb, called "555". However, other graphics cards use rrrrrrgggggbbbbb to represent these three colors because people are more sensitive to green, this is "565". So if you put the screen to 16-bit color when you initialize, you can't use RGB () when you fill (but set transparencies). To write a program to determine the graphics card is "555" or "565" Convert the color depending on the situation. But if we want to fill the pure red, green, blue, and black, you don't have to be so troublesome, so you can: MakeRect (x, x, x, x); // Specify Ddpixelformat DDPF; Ddpf.dwsize = sizeof (ddpf); LPDDSBuffer-> getpixelformat (& ddpf); DDBLTFX DDBLTFX; DDBLTFX.DWSIZE = SizeOf (DDBLTFX); DDBLTFX.DWFILLCOLOR = (word) ddpf.dwgbitmask; // If pure red is dwrbitmask, // Pure Blue is dwbbitmask LPDDS-> BLT (& Rect, NULL, & Rect, DDBLT_WAIT | DDBLT_COLORFILL, & DDBLTFX); If we want to fill pure black, you can set DWFillColor to 0 and remove all statements related to DDPF. 5.2 Output Text In order to output text to the page, we must first get the HDC of the page (device description handle), then call the Windows GDI function to output text to the page. Since the DirectDraw function change page will not be used after the handle is obtained, the handle is immediately released after the text is output. HDC HDC; IF (LPDDSXXX-> getDC (& HDC) == DD_OK) / / Take a handle { SetBKColor (HDC, RGB (0, 0, 255)); // Setting the text background color, if it is transparent, this sentence is changed to: // setBkmode (HDC, Transparent); SetTextColor (HDC, RGB (255, 255, 0)); // Set text color Textout (HDC, 100, 400, Text, Strlen (Text)); // handle, left upper corner X, upper left corner Y, // Text (char *), text length LPDDSXXX-> ReleaseDC (HDC); // Release the handle } 5.3 GDI Since DirectDraw does not provide a statement, line, circle, etc., so we have to complete these work with the Windows GDI function. Just like output text, we must get the hdc of the page first: HDC HDC; LPDDSXXX-> GetDC (& HDC); The drawpoint is the simplest, setpixel (HDC, X, Y, RGB (R, G, B)); you can draw a point in which a specified color is drawn at the screen (x, y) coordinate. If you need to draw a line, we need to create a "brush": HPEN HPEN = Createpen (PS_SOLID, 5, RGB (R, G, B)); Createpen's first parameter meaning is a brush style, a commonly used PS_SOLID (Normal Brush) and PS_DOT (a brush consisting of interposed points, need to set the brush width 1). The second parameter is the width of the brush, and the third parameter is the color of the brush. Then put the brush to HDC: SELECTOBJECT (HDC, HPEN); Mobile brush to (x1, y1): MoveToex (HDC, X1, Y1, NULL); Painting from the drawing start position to (X2, Y2) coordinate: LINETO (HDC, X2, Y2); Some commonly used drawing statements are listed below, the usage methods and the picture are similar, and the fixing pen can be used: Rectangle (HDC, X1, Y1, X2, Y2); // Draw a rectangle Ellipse (HDC, X1, Y1, X2, Y2); // Painting Ellipse It is worth noting that the graphics we draw will be filled by a "brush", and the simpler method of using the simplest monochrome brush is: Hbrush Hbrush = Createsolidbrush (RGB (R, G, B)); // Create a brush SelectObject (HDC, Hbrush); // Using a brush After painting, we have to remember the release of HDC: LPDDSXXX-> ReleaseDC (HDC); 5.4 Program Example 5.5 lock page After learning the above knowledge, there are still many special effects we can't do, such as fading into fade, translucent, etc., because the ready-made functions in DirectDraw do not have these features at all. Of course, to do these things, there is still a way, use idirectdrawsurface7 :: lock () to make us feel like this, because this function allows us to directly modify the page. The LOCK () function is as follows: HRESULT LOCK LPRECT LPDESTRECT, LPDDSURFACEDESC2 LPDDSURFACEDESC, DWORD DWFLAGS, Handle HEVENT ); The first parameter is a pointer to a Rect, which specifies the page area that will be locked. If this parameter is NULL, the entire page will be locked. The second parameter is an address of a DDSurfaceDesc2 structure that will be populated with the page. The third parameter, that is, DWFLAGS, or gives it DDLOCK_WAIT as before. The fourth parameter specifies NULL. Now give an example to explain how to use LOCK (), our goal is to translate LPDDSBACK on the LPDDSBuffer. Let's take a look at the complete lock screen (note that this section is discussed on the 24 and 32-bit color below): DDSURFACEDESC2 DDSD, DDSD2; // DirectDraw page description ZeromeMory (& DDSD, SIZEOF (DDSD)); // DDSD to be emptied DDSD.dwsize = sizeof (ddsd); // DirectDraw in this way ZeromeMory (& DDSD2, SIZEOF (DDSD2)); DDSD2.DWSIZE = SizeOf (DDSD2); LPDDSBuffer-> Lock (Null, & DDSD, DDLOCK_WAIT, NULL); // Lock! LPDDSBACK-> LOCK (NULL, & DDSD2, DDLOCK_WAIT, NULL); BYTE * bitmap = (byte *) DDSD.lpsurface; // Lock The page information is exist here, please pay attention // This pointer may be different after Lock ()! BYTE * bitmap2 = (byte *) DDSD2.LPSURFACE; After locking the page, the storage format of the Bitmap array is such that the R / g / B value of the point of the coordinate (x, y) on the page is present, and there is bitmap Y * DDSD.LPITCH X * 4, Y * DDSD.LPITCH X * 4 1, Y * DDSD.LPITCH X * 4 2; if the number of coordinates (x, y), if 24-bit color, the point of the coordinate (x, y) R / g / b There is a value of Bitmap Y * DDSD.LPITCH X * 3, Y * DDSD.LPITCH X * 3 1, Y * DDSD.LPITCH X * 3 2. In fact, LPITCH is the width of the line. So, now we can play an imagination, do all the effects you want, such as lighting effect (to change the brightness of a target page by illumination table)! The following is the next code (32-bit color): INT POS; For (int y = 0; y <480; y ) { For (int x = 0; x <640; x ) { Bitmap [POS] = (Bitmap [POS] Bitmap2 [POS]) >> 1; // Reform R POS ; Bitmap [POS] = (Bitmap [POS] Bitmap2 [POS]) >> 1; // change G POS ; Bitmap [POS] = (Bitmap [POS] Bitmap2 [POS]) >> 1; // Reform B POS = 2; // to the next R } POS = DDSD.LPITCH; } LPDDSBACK-> UNLOCK (NULL); // unlock! LPDDSBuffer-> UNLOCK (NULL); Since DirectDraw is to lock the page after using LOCK, we cannot modify the page without using unlock (). So use LOCK () to quickly like Ulock () as you. Unlock's method is simple, LPDDSXXX-> Unlock (LPRECT LPDESTRECT). 5.6 program speed The above program seems to be very simple, but the speed is very likely to be very slow, even if you use the compilation and write, it will not be much faster. The reason is that the reading is very slow, and the speed of writing is slower than written memory. Solving this problem is: (1) Put all the pages except the home page in memory (initialization page, then DDSCAPS_OFFSCREENPL in DDSD.DDSCAPS.DWCAPS, or (|), DDSCAPS_SYSTEMMORY, of course, will also change the page of the main page. : Ddsd.dwsize = sizeof (ddsd); DDSD.DWFLAGS = DDSD_CAPS; DDSD.DDSCAPS.DWCAPS = DDSCAPS_PRIMARYSURFACE; The background buffer changes to a normal off-screen page. Another benefit of this is that you Lock () will always get the page pointer after once, and then a unlock () can use BLT. So you have two means of changing the page. (2) Change FLIP () and non-transparent color BLTFAST () to copy directly with Memcpy (). Note that you should copy a line, such as the full screen copy of 640x480x24 colors is like this: BYTE * PSRC = (Byte *) DDSD_SRC.LPSURFACE; // Source page BYTE * PDEST = (Byte *) DDSD_DEST.LPSURFACE; // Target page for (int y = 0; y <480; y ) { Memcpy (PDEST, PSRC, 1920); // is 2560 = 640 * 32/8 if 32 color PSRC = DDSD_SRC.LPITCH; / / Move to the next line PDEST = DDSD_DEST.LPITCH; / / Move to the next line } In fact, Memcpy () has the room for further "pressing", which is ultra-high-speed MEMCPY () implemented with SSE instructions. Enjoy the perfect speed in your program (fast than 80%), huh, huh (nqwords is to copy how much 8 bytes, pay attention to it, should be 8!). Void Qmemcpy (Void * DST, VOID * SRC, INT NQWORDS) { #define cacheblock 1024 // How many qwords in a block? // Modify this value to achieve higher speed INT N = ((int) (nqwords / cacheblock) * cacheblock; INT m = nqwords-n; IF (n) { _ASM // Copy the intensity block { MOV ESI, SRC MOV EDI, DST MOV ECX, N / / How to copy how many blocks Lea ESI, [ESI ECX * 8] Lea EDI, [EDI ECX * 8] NEG ECX Mainloop: Mov Eax, Cacheblock / 16 Prefetchloop: MOV EBX, [ESI ECX * 8] // Read this loop MOV EBX, [ESI ECX * 8 64] // Read Reading Loop Add ECX, 16 Dec EAX JNZ prefetchloop Sub ECX, Cacheblock Mov Eax, Cacheblock / 8 WriteLoop: MOVQ MM0, QWORD PTR [ESI ECX * 8] MOVQ MM1, QWORD PTR [ESI ECX * 8 8] MOVQ MM2, QWORD PTR [ESI ECX * 8 16] MOVQ MM3, QWORD PTR [ESI ECX * 8 24] MOVQ MM4, QWORD PTR [ESI ECX * 8 32] MOVQ MM5, QWORD PTR [ESI ECX * 8 40] MOVQ MM6, QWORD PTR [ESI ECX * 8 48] MOVQ MM7, QWORD PTR [ESI ECX * 8 56] Movntq QWORD PTR [EDI ECX * 8], MM0 Movntq QWORD PTR [EDI ECX * 8 8], MM1 Movntq QWORD PTR [EDI ECX * 8 16], MM2 Movntq QWORD PTR [EDI ECX * 8 24], MM3 Movntq QWORD PTR [EDI ECX * 8 32], MM4 Movntq QWORD PTR [EDI ECX * 8 40], MM5 Movntq QWORD PTR [EDI ECX * 8 48], MM6 Movntq QWORD PTR [EDI ECX * 8 56], MM7 Add ECX, 8 Dec EAX JNZ WriteLoop OR ECX, ECX JNZ Mainloop } } IF (m) { _asm { MOV ESI, SRC MOV EDI, DST MOV ECX, M MOV EBX, NQWORDS Lea ESI, [ESI EBX * 8] Lea EDI, [EDI EBX * 8] NEG ECX Copyloop: PrefetChnta [ESI ECX * 8 512] // Read Read MOVQ MM0, QWORD PTR [ESI ECX * 8] MOVQ MM1, QWORD PTR [ESI ECX * 8 8] MOVQ MM2, QWORD PTR [ESI ECX * 8 16] MOVQ MM3, QWORD PTR [ESI ECX * 8 24] MOVQ MM4, QWORD PTR [ESI ECX * 8 32] MOVQ MM5, QWORD PTR [ESI ECX * 8 40] MOVQ MM6, QWORD PTR [ESI ECX * 8 48] MOVQ MM7, QWORD PTR [ESI ECX * 8 56] Movntq QWORD PTR [EDI ECX * 8], MM0 Movntq QWORD PTR [EDI ECX * 8 8], MM1 Movntq QWORD PTR [EDI ECX * 8 16], MM2 Movntq QWORD PTR [EDI ECX * 8 24], MM3 Movntq QWORD PTR [EDI ECX * 8 32], MM4 Movntq QWORD PTR [EDI ECX * 8 40], MM5 Movntq QWORD PTR [EDI ECX * 8 48], MM6 Movntq QWORD PTR [EDI ECX * 8 56], MM7 Add ECX, 8 JNZ COPYLOOP sfnce EMMS } } Else { _asm { sfnce EMMS } } } Similarly, MEMSET can also be optimized with SSE instructions, and the code is here (nqword should be 8): Void Qmemset (Void * DST, INT C, Unsigned long nqwords) { __ASM { MOVQ MM0, C PUNPCKLBW MM0, MM0 PunpckLWD MM0, MM0 PUNPCKLDQ MM0, MM0 MOV EDI, DST MOV ECX, NQwords Lea EDI, [EDI ECX * 8] NEG ECX MOVQ MM1, MM0 MOVQ MM2, MM0 MOVQ MM3, MM0 MOVQ MM4, MM0 MOVQ MM5, MM0 MOVQ MM6, MM0 MOVQ MM7, MM0 LoopWrite: Movntq [EDI ECX * 8], MM0 Movntq [EDI ECX * 8 8], MM1 Movntq [EDI ECX * 8 16], MM2 Movntq [EDI ECX * 8 24], MM3 Movntq [EDI ECX * 8 32], MM4 Movntq [EDI ECX * 8 40], MM5 Movntq [EDI ECX * 8 48], MM6 Movntq [EDI ECX * 8 56], MM7 Add ECX, 8 Jnz loopwrite EMMS } } (3) Since DirectDRAW is provided with transparent color BLTFast () at this time is not optimistic, we can use RLE to compress the transparent color, and write a function that can transmit a transparent color. You may have already heard RLE compression, and it doesn't matter if you haven't heard it. If you define transparent colors (255, 0, 255), then such a strip: (255, 0, 255), (255, 0, 255), (255, 0, 255), (100, 23, 43) (213, 29, 85), (255, 0, 255), (34, 56, 112), (255, 0, 255), (255, 0, 255) can be stored in this way: The first is 4 transparent points, then we put one 0 at the beginning, indicating that it is started by transparency, otherwise you need to put one. Then 0, 12, (indicating that the 4x3 = 0012 bytes must be skipped), then 0, 2, (ie there is 002 image points), 100, 23, 43, 213, 29, 85, 0, 3 (You need to skip 003 bytes), 0, 1 (after 001 image points), 34, 56, 112, 0, 6 (the 006 bytes must be skipped). This means that the original image of the original item is required in this way, and the BLT is faster because there is no need to determine if each point is determined. In general, RLE compression is only performed within each line of the image (rather than the entire image as an overall) to facilitate programming. If you still need a faster speed without the file size, there is a comparative "evil" solution, its idea is to automatically generate thousands of assignment instructions with another program to implement a BLT. This approach can save a lot of judgment and time consumption of jump instructions, and the speed is clearly reached. It seems that there seems to be more rewritten, and the part of the change is not much. This will soon change the speed of the entire program, but it is not well to meet the requirements of full screen special effects, because full-screen special effects are really time, only with compilation and MMX and other instructions to override speeds. So in the following chapter, we will introduce embedded assembly and MMX instructions. Of course, you can use the existing code of 5.7 directly. 5.7 Special Effect In order to facilitate everyone to make a game, some common special effects are listed here. If you want to know how these codes work, please read the sixth chapter. The following program is based on 640x480x32 color, if the resolution you use is easy to modify, but if the number of colors used is very troublesome. 5.7.1 DD and high Dreeing and highlighting is two very effective effects and there are many applications in the game. __INT64 MASK = 0x4040404040404040; // to be dimmed or high, should // 0xabcdefghabcdefgh format // change a change to make a color light effect INT TT = Y * LP X * 4; // LP should be the LPITCH, X and Y of the background cache should be the left left of the area to be processed. // angle coordinates __ASM { MOVQ MM1, MASK; MOV EAX, PS; // PS should be a pointer to the background cache (LPSURFASE) Add Eax, TT; MOV ECX, H; // H is the height of the area to be processed Outloop: Push Eax; MOV EBX, W; // W is the width of the area to be processed / 2 INNLOOP: MOVQ MM0, [EAX]; PSUBUSB MM0, MM1; // If you want to increase, change the psubusb to PaddusBMOVQ [EAX], MM0; Add Eax, 8; Dec EBX; JNZ INNLOOP; POP EAX; Add Eax, LP; Dec ECX; JNZ OUTLOOP; EMMS; } 5.7.2 fade in fade out Atmosphere is often used when switching the game scene, achieving it is very simple, as long as you continue to implement the program of 5.7.1. 5.7.3 translucent Semi-translation is a very dazzling effect, whose principle is to make a average of each color value of the two images. Let's talk about the easiest way, the fastest speed, and the most common 50% translucent approach: (1) Cut the RGB value half of the RGB value in advance using the image processing software in advance image processing software. (2) Execute this code in the refresh screen function: __INT64 MASK = 0x7f7f7f7f7f7f7f; INT TT = Y * LP X * 4; // LP should be the LPITCH, X and Y of the background cache should be the left left of the area to be processed. // angle coordinates __ASM { MOVQ EDX, PB; // PB should be a pointer to the page of the storage special effect image MOV EAX, PS; // PS should be a pointer to the background cache (LPSURFASE) Add Eax, TT; MOV ECX, H; // H is the height of the area to be processed Outloop: Push Eax; Push EDX; MOV EBX, W; // W is the width of the area to be processed / 2 INNLOOP: MOVQ MM0, [EAX]; PSRLW MM0, 1; MOVQ MM1, [EDX]; PAND MM0, MASK; // Realize "PSRLB" Paddusb mm0, mm1; MOVQ [EAX], MM0; Add Eax, 8; Add EDX, 8; Dec EBX; JNZ INNLOOP; POP EDX; POP EAX; Add Eax, LP; Add Edx, LP1; // LP1 LPITCH should be stored in the page of the product Dec ECX; JNZ OUTLOOP; EMMS; } 5.7.4 Light Initialized lightout at the beginning of the program: Voidinit () { // Baselisht is the basic light table Baselight = new unsigned char [307200]; //640 * 480 = 307200 For (int i = 0; i <640; i ) For (int J = 0; J <480; J ) Baselight [i j * 640] = SQRT ((i-320) * (i-320) (J-240) * (J-240)) * 224/400; // Darkle Brightness minus 224 } Press a light table (LightTable []) to reduce the brightness of the image on the page: BYTE * PL = (Byte *) & LightTable [0]; __ASM { MOV ECX, 480; MOV EDI, PB; // PB should be a pointer (LPSURFASE) in the background MOV ESI, PL; Outloop: Push EDI; MOV EBX, 320; // 640/2 = 320 INNLOOP: MOVQ MM0, [EDI]; MOVQ MM1, [ESI]; // Read the brightness of eight points, set it to hgfedcba PUNPCKLBW MM1, MM1; // DDCCBBAA Punpcklbw mm1, mm1; // bbbbaaa, two points PSUBUSB MM0, MM1; // minus brightness MOVQ [EDI], MM0; // put back (in memory is aaaabbbbbbBBBBBB) Add EDI, 8; Add ESI, 2; // Lower two points Dec EBX; JNZ INNLOOP; POP EDI; Add EDI, LP; // LP should be LPITCH cached in the background Dec ECX; JNZ OUTLOOP; EMMS; } 5.7.5 Dynamic Light First of all, we must figure out a problem: if the brightness of a bunch of light is 200, the brightness of a bunch of light is 150, what is the result after mixing? It is impossible to be 200 150 = 350 because this exceeds the upper bound of 255. The answer should be (1- (255-200) / 255 * (255-150) * 255) * 255, ie 255- (255-200) * (255-150) / 255 = 232. If we use the "darkness" instead of brightness, the algorithm is simpler, as long as the two darkness multiply, divided by 255. In fact, the trouble is not to calculate the final "darkness", but to handle the situation when the light range is exceeded. Suppose BaseLight [] is ambient illumination table, Light [] is a light table to be added, then it is obvious to be a tailoring when the position of the lamp is close to the edge of the screen. Since the MMX directive can calculate the illumination of 4 points at a time, the problem becomes more complicated. So how do you do it best? It is a convenient way to expand the BaseLight [] to the pixels, but you will add an instruction when you put the light. Please see the code below: Voidinit () // Initialized Light { Lighttable = new unsigned char [310080]; // Actual light table, 646 * 480 = 310080 Baselisht = new unsigned char [310080]; // Basic light table For (int i = 0; i <646; i ) // multi-byte to prevent offshore { For (int J = 0; J <480; J ) { BaseLight [i j * 646] = SQRT ((i-323) * (i-323) (J-240) * (J-240)) * 224/400; } } Smalllight = new unsigned char [10004]; / / light illuminating table, open 4 bytes to prevent offshore For (i = 0; i <100; i ) { For (int J = 0; j <100; j ) { INT DIS = SQRT ((I-50) * (i-50) (J-50) * (J-50)) * 255/45; IF (Dis <255) SmallLight [i J * 100] = DIS; Else SmallLight [i j * 100] = 255; } } For (i = 10000; i <10004; i ) SmallLight [i] = 255; } Every time we refresh the screen, we must first copy Baselisht to LightTable: Memcpy (LightTable, Baselisht, 310080); Then add the SmallLight to LightTable: P1 = & (LIGHTTABLE [Y * 646 X]); // Y and X are SmallLight [] on the top left corner coordinate on the screen P2 = & (SmallLight [Rect.top * 100 Rect.Left]); // Rect is a smallLight (] // Effective area // Tailor ideas can be referred to Section 4.6 procedures INT Height = Rect.Bottom-Rect.top; int width = (Rect.right-Rect.Lep 3) / 4; // plus 3 to ensure all processing IF ((Height> 0) && (Width> 0))) { __ASM { PXOR MM2, MM2; // Put MM2 Clear 0 MOV EDI, P1; MOV ESI, P2; MOV EBX, Height; _OLOOP: MOV ECX, Width; Push EDI; PUSH ESI; _iloop: MOVQ MM0, [EDI]; MOVQ MM1, [ESI]; PUNPCKLBW MM0, MM2; PUNPCKLBW MM1, MM2; Pmullw mm0, mm1; // Multipouth PSRLW MM0, 8; // divided by 256, because it is obviously faster than 255 Packuswb mm0, mm2; MOVD [EDI], MM0; // Go back Add ESI, 4; Add EDI, 4; Dec ECX; JNZ_ILOOP; POP ESI; POP EDI; Add ESI, 100; Add EDI, 646; Dec EBX; JNZ _OLOOP; EMMS; } } The procedure of the light illumination LightTable is changed to: BYTE * PL = (Byte *) & LightTable [0]; __ASM { MOV ECX, 480; MOV EDI, PB; // PB should be a pointer (LPSURFASE) in the background MOV ESI, PL; Outloop: Push EDI; MOV EBX, 320; // 640/2 = 320 INNLOOP: MOVQ MM0, [EDI]; MOVQ MM1, [ESI]; // Read the brightness of eight points, set it to hgfedcba PUNPCKLBW MM1, MM1; // DDCCBBAA Punpcklbw mm1, mm1; // bbbbaaa, two points PSUBUSB MM0, MM1; // minus brightness MOVQ [EDI], MM0; // put back (Aaaabbbbbb in memory) Add EDI, 8; Add ESI, 2; // Lower two points Dec EBX; JNZ INNLOOP; POP EDI; Add EDI, LP; // LP should be LPITCH cached in the background Add ESI, 6; Dec ECX; JNZ OUTLOOP; EMMS; } 5.7.6 Light System In order to achieve lighting systems similar to Diablo II, we have many jobs to do. The core task is to solve the exact illumination problems of the walls and wizards and the wall of the wall to the light. If you use the code of the 5.7.5 code without extra processing of the walls and wizards, you will first find that their illumination is not normal - there is no three-dimensional sense, because they have stretched in the z direction, The distance from the origin can not take their position on the screen; the second question is that the wall actually can't block light. Ok, let's take a look at how to do it. The first question is better to solve, if the walls and wizards are high, as long as a "If the bottom is N, if the bottom light is N so high h, how much is", "then refresh the screen, first 5.7.5 The code processing surface, then press this table and the illumination fake and the wall; if they are not high, it can be approximated, and all the lights are drawn in the bottom of the bottom, Diablo II is doing this. . The second question is more troublesome. Since it is obviously impossible to judge the wall blocking directly on the screen, it is best to divide the screen into a small grid, and then determine whether they have been covered by the wall. If it is in the light table, this lattice A darker value of all points, and finally the interpolation operation makes the light transition smooth. It's hard to do, look at the specific code: 5.7.7 Weather effect Let's talk about the flame special effects and lightning effects, which is generally achieved by a small animation that has been made. Let's go to Zhengzheng, first look at how the rain is achieved. This effect is mainly composed of raindrops and water waves. The raindrops can be achieved by drawing some directions (basically all with the wind), brightness, and the length is basically the same as the white lines, and in the Timer to move toward their direction; the water wave special effect is more troublesome, and will be detailed later; Also add a little sudden fade out of the simulated lightning effect. Snow is also a common weather effect. Chapter 6 Accelerating Games Magic In order to accelerate the game, you will be very mysterious in the assembly language. In fact, if you learn, it will find that it is not as difficult as it is. Especially embedded compilation, because it is closely combined with C , you don't have to consider a lot of cumbersome details (such as the writing of the input and output functions), learning is easier. Using embedded compilation, especially using MMX instructions, you can greatly improve the speed of common effects in various games, which is very important to compose a beautiful game. There is also a special and interesting use of the assembly language: you can observe and understand the assembly code generated by VC , thereby better understanding the C language itself and optimizing code. 6.1 Introduction to the embedded assembly In advanced languages, we can use various statements without any accusation, and then convert them to machine instructions after a very complex compilation process. In fact, there are not many instructions that the processor itself can handle; even worse, most instructions cannot be directly applied to variables in memory, and to with the register of the intermediate storage unit (you can see the register as a variable) . There are not many registers of the Pentium level processor, only 8 32-bit general registers, respectively, called EAX, EBX, ECX, EDX, EBP, ESP, EDI, ESI. The low 16 bits of each universal register is called AX, BX, CX, DX, BP, SP, DI, SI, respectively, respectively. Ax, BX, CX, DX high is called AH, BH, CH, DH; low 8 bits known as Al, BL, Cl, DL. Note EBP and ESP should not be used in the inline assembly, which stores important stack information. There is also a very important register called flag register (EFLAGS), indicating the individual properties of the calculation result, you can't read or modify it directly. These attributes are: no overflow / overflow (OF), positive / negative (sf), non-zero / zero (ZF), even / odd (PF), non-carry / carry (CF), etc. To indicate a symbol integer in the assembly language, you need to write a binary form of the absolute value of the integer. If the number is positive or zero, it has been obtained, otherwise it will be reverse (0-> 1, 1-> 0) After adding one. So a 8-bit register can be represented by a symbol integer range from -128 to 127. Similar to C , assembly language provides a method of obtaining memory referring to a pointer, which is called "addressing". The usage is very simple, like this: [Register Register * 1/2/4/8 32-bit Immediate] You can get the number of this location. For example, if there is an array unsigned short a [100], and the address of the A [0] is stored in EAX, then [EAX 58] is a value of a [29]; if EBX = 9, then [EAX EBX * 2 4] will be a value of A [11]. So how do you load the address of a variable into the register? It will be introduced later. The use of embedded assembly is: _asm { Statement // can be added without a semicolon } You can put it in any location in the program, very flexible. 6.2 Basic Directive Basic instructions do not affect the logo register. The first instruction is a transfer command: MOV DEST, SRC. Its action is to assign DEST to the value SRC. Where DEST and SRCs can be integer (called immediate), variables, or [Address] (memory), registers. It is important to note that there is not allowed: You can never give the contents of the memory or register in the assembly language (have you seen the statement like 5 = A?); You cannot directly assign the memory content directly Another memory must be implemented with the register as an intermediate variable. It is important to note about MOV that DEST and SRC must be 32-bit / 16-bit / 8 digits, namely the same size. It is worth noting that the storage mode in memory is reversed in bytes, ie, if the byte stored by the memory address 0000 is 5F, the address 0001 stored by 34, address 0002 stored bytes Is 6a, address 0003 stored bytes is C4, then the word (Word, 16 bits) stored at address 0000 is 345F, double word (DWORD, 32 bits) is C46A345F. The second instruction is an address loading instruction: Lea A, B. Its action is to load the address of the B variable into the A register (A required to be 32 bits). It should be noted that it is not like LEA EAX, Temp [5] to directly adjust the address of an element in the group. This instruction can also be used to make a simple operation, consider the following statement: LEA EAX, [EBX ECX * 4 8], this statement can assign the value of EBX ECX * 4 8 to EAX. OK, let us see a program that can add two positive integers: #include Using namespace std; // This program also shows how embedded assembly should use pointers in C void main () { Unsigned Int A, B; CIN >> A; CIN >> B; INT * C = & a; __ASM // The following is embedded assembly ... { A address stored in MOV EAX, C; // C -> EAX MOV EAX, [EAX]; // a value -> EAX // Note Direct MOV EAX, [C] is wrong MOV EBX, B; / / can be assigned directly to EBX directly Lea Eax, [EAX EBX]; Mov A, EAX; // can directly use the value of Eax -> A } // Embedded assembly part end ... Cout < } The third instruction is the exchange instruction, and the form is XCHG A, B. At least one of A and B must be a register. If you want to exchange data in both memory, use the register as an interior. Then there are two extended delivery instructions, with two, for MOVSX DEST, SRC, and MOVZX DEST, SRC, and their use is assigned to DEST in the SRC. At this time, you can assign the contents of the register of the word length to a long register, and it can't. Everyone will find that 8 universal registers cannot meet the requirements of programming. In order to solve this contradiction, the intelligence of the stack is introduced. You can imagine the stack imagination as a box of boxes, and put a box in the top of the existing box, and the outlet (POP) can take out the top of the existing box. Take a look at the instruction below: Push Eax // EAX Put, stack is eax Push EBX // EAX Put, stack is EAX EBX Push Ecx // EAX Put, stack is EAX EBX ECX POP EBX / / EBX = ECX, stack is EAX EBX POP EAX / / EAX = EBX, stack is eax POP ECX // ECX = EAX, stack empty It can be seen that the stack can not only easily temporarily store data but also adjust their order. 6.3 Arithmetic Directive Moderates Most of the Math Directive Impact Sign Register. These instructions are easier to understand, and now it is now listed: Table 6.1 CLC CF = 0 STC CF = 1 CMC CF = 1-CF Add A, B A = A B (the result may have a weird result, and CF 1 is set) ADC A, B A = A B CF (plus carry) SUB A, B A = A-B (If the result is less than 0, it will add 2 16 or 32 times, and CF 1 is set) SBB A, B A = A-B-CF (subtracted) INC A A Dec a A- - NEG A A = -a Mul a EAX = EAX * A low 32-bit, EDX = high 32 digits Example: MOV Eax, 234723 Mov Edx, 12912189 Mul EDX; EAX = 2835794967 EDX = 705 Div A EAX = (EDX EAX) / A of the business, EDX = remainder Example: MOV Eax, 12121 Mov EDX, 2 At this time (EDX EAX) = 8589946713 MOV EBX, 121 Div EBX; EAX = 70991295 EDX = 18 Imul / IDiv DEST, SRC has symbolic multiplier / division, dest = DEST multiplication / in addition to SRC IMUL / IDIV DEST, S1, S2 have symbolic number / division, DEST = S1 multiplication / except S2 In order to let everyone understand the logo, please see two programs (the number of appearances is a hexadecimal number): Table 6.2 Directive CF ZF SF OF PF AX or BX Mov AX, 7896???? Add Al, AH 1 0 0 0 0 780E Add Ah, Al 0 0 1 1 0 860E Add Al, F2 1 1 0 0 1 8600 Add Al, 1234 0 0 1 0 0 9834 MOV BX, 9048???? 9048 SUB BH, BL 0 0 0 1 1 4848 SUB BL, BH 0 1 0 0 1 4800 SUB BL, 5 1 0 1 0 0 48FB SUB BX, 8F34 1 0 1 1 0 B9C7 6.4 Logic and Shift Instruction The logic instruction will clear the OF and CF in the flag register. Table 6.3 NOT A A = ~ a (note that NOT is different from NEG!) And A, B A = A & BOR A, B A = A | B XOR A, B A = a ^ b The following is a shift command, where x can be 8-bit immediate or CL registers. Table 6.4 SAL (also written into SHL) a, X to move a left to the X-bit, CF = one number of moved Air space 0 make up SAR A, X will have a sign of X-bit, CF = one number of moved The symbol of the vacation is set with 0/1. SHR A, X will be unsigned to move the X-bit, CF = the number of moved Air space 0 make up ROL A, X will be left left left (the number of out of the left is back from the right right) ROR A, X will be changed to the A cycle (the number out of the right is back from the left) RCL / RCR A, X puts CF at the left side of the target and then circulating left / right SHLD A, B, X move a left to the X-bit, and the empty outlet is filled with B high-end M bit. Example: SHLD EDX, EAX, 16 can put the high 16 bits of EAX in DX. SHRD A, B, X move a right to the X-bit, empty outlet B-lower M bits filled 6.5 Comparison, Test, Transfer and Circulation Directive The comparison and test instructions are basically always used in conjunction with the transfer instruction, and the forms are CMP A, B and Test A, B. The CMP is actually changed according to the value of the value of the A-B but does not change A and B, which can detect the size of the two numbers. TEST is also changed according to the value of A & B changes the flag register, and the A and B are also not changed. This instruction can be used to test which bit is 1 in 1. After performing these instructions, the condition transfer can be implemented immediately because the conditional transfer statement will determine whether or not the transfer is determined according to the flag register. The use of transfer instructions is like this: __ASM { _addax: add ax, 1; // _ addax is the label JMP _Addax; } Transfer instructions are: JMP unconditional transfer JE / JZ ZF = 1 Transfer JNE / JNZ ZF = 0 Transfer JS sf = 1 transfer JNS sf = 0 transfer JO OF = 1 Transfer JNO OF = 0 Transfer JP / JPE PF = 1 Transfer JNP / JPO PF = 0 Transfer Transfer according to the two unsigned number of relationships: JA / JNBE is greater than time transfer (CF or ZF = 0) JBE / JNA is not more transferred (CF or ZF = 1) JB / JNAE / JC is less than time transfer (CF = 1) JNB / JAE / JNC is not less than time transfer (CF = 0) Based on two symbol digital relations transfer: JNLE / JG is greater than time transfer ((sf is or of) or zf) = 0) JLE / JNG is not greater than the transfer ((sf is or of) or zf) = 1) JL / JNGE is less than time transfer (sf different or = 1) JNL / JGE is not less than time transfer (sf is too = 0) Special transfer statement: JECXZ CX = 0 Transfer In order to remember so many instructions, you only need to know, the relationship between unsigned numbers is called Above, Equal, Below, represents the corresponding relationship between the corresponding relationships between the symbol numbers, respectively; Great, Equal, Less. In fact, some metastasis can be avoided. For example, do you want to use a number of absolute values? Please see a program: Mov Edx, EAX SAR EDX, 31 // EDX is now full of Eax symbolic XOR Eax, EDX Sub Eax, EDX What should I use to use a bigger one of the two numbers? However, you can use the flag as the solution below, it is really absolutely: SUB EBX, EAX SBB ECX, ECX // If EBX ≥ Eax, now ECX = 0, otherwise ECX = ffffffffF And ECX, EBX Add Eax, ECX The following program implements if (a! = 0) a = b; ELSE A = C; CMP EAX, 1 SBB EAX, EAX XOR ECX, EBX And Eax, ECX XOR EAX, EBX The loop statement is commonly used is loop, which is equivalent to DEC CX plus JNZ. Look at a compilation of a compilation: bubbling sort. #include Using namespace std; #define array_size 10 INT A [Array_Size] = {42, 73, 65, 97, 23, 59, 18, 84, 36, 6}; void main () { INT * P; P = & a [0]; P -; __ASM { MOV ESI, P; MOV ECX, Array_Size; _outloop: Mov EDX, ECX; _inloop: MOV EAX, [ESI ECX * 4]; // an int 4 byte MOV EBX, [ESI EDX * 4]; CMP EAX, EBX; JNB _noxchg; // does not exchange MOV [ESI ECX * 4], EBX; MOV [ESI EDX * 4], EAX; _NOXCHG: Dec EDX; JNZ _INLOOP; Loop_outloop; } For (int i = 0; i <10; i ) Cout < << " MOV EAX, R; Add Eax, 100; MOV R, EAX; MOV EAX, G; Add Eax, 100; // !!! Mov G, EAX; MOV EAX, B; Add Eax, 100; MOV B, EAX; The CPU will appear as the result of the marked, since the g value will not stay on 255. The use of color saturation functions can be left on 255, and it will also stay at 0 when diminishing. You can use the following function to detect whether the CPU supports MMX instructions: Int Checkmmmx () { INT ISMMX = 0; __ASM { MOV EAX, 1; CPUID; TEST EDX, 00800000H; JZ Notsupport; MOV ISMMX, 1; NotSupport: } Return ISMMX; } By the way, see how to determine whether the CPU supports SSE: INT CHECKSSE () { INT ISSS = 0; _asm { MOV Eax, 1 cpuid SHR EDX, 0x1A JNC Notsupport MOV ISSS, 1 NotSupport: } Return ISSS; } Here are a few of the most basic MMX instructions: EMMS Since the floating point arithmetic unit occupies the MMX command, you should remember the execution of this instruction to release the floating point arithmetic unit after using the MMX instruction. MOVD DEST, SRC DEST is the MMX register / universal register / memory, and SRC is MMX register / universal register / memory. Its function is to transmit 32 bits in the SRC to the lower 32-bit Class DEST of the DEST. MOVQ DEST, SRC DEST is the MMX register / memory, and SRC is MMX register / memory. Its role is to transfer 64 bits in the SRC to DEST. 6.7 MMX Directive Set Arithmetic and Comparison Directive MMX provides some very easy to use arithmetic instructions, which can greatly speed up 2D special effects. The first is the addition and subtraction command, a total of 8, the form is PADD / PSUBXY DEST, SRC, where X is empty / "s" / "US", which represents colorless saturation, symbolic color saturation, no symbolic color saturation. Y is "B" / "w" represents one byte or word as an arithmetic unit, which is simultaneously executed 8 byte addition or 4 word addition. If X is empty, Y is also D, representing 2 double word additions simultaneously. Dest and SRC are required for the MMX register. Three examples: MM0 = 008 000 005 000 255 000 141 045 MM1 = 000 057 005 000 131 002 PADDB MM0, MM1 MM0 = 008 057 010 000 000 016 047 MM0 = 008 000 005 000 255 000 141 045 MM1 = 000 057 005 000 131 002 PADDSB MM0, MM1 MM0 = 008 057 010 000 255 000 255 047 MM0 = 001234 000010 000005 008516 MM1 = 000001 000020 000001 009343 PSUBSW MM0, MM1 MM0 = 001233 000000 000004 000000 Then there are three multiplications, there are three: PMaddwd Dest, SRC Assume that SRC is A, B, C, D; DEST is E, F, G, H. After execution, DEST is A * E B * F, C * G D * h. Pmulhw Dest, SRC Assume that SRC is A, B, C, D; DEST is E, F, G, H. After the execution, DEST is a * E high 16 bits, b * f high 16 bits, C * g is 16 bits, D * H high 16 bits. Pmullw DEST, SRC The instruction is almost the same as PMulhw, that is, it is changed to 16 bits. These three instructions have applications in image deformation and rotation, translucent. The following is a comparison instruction: PCMPEQX DEST, SRC X = B / W / D DEST and SRC are MMX registers When the DEST is equal to SRC, the corresponding DEST is all, otherwise the corresponding DEST total 0 is set. example: MM0 = 012321, 000912, 023849, 005634 MM1 = 032123, 000912, 022234, 005634 PCMPEQD MM0, MM1 MM1 = 000000, 065535, 000000, 065535 PCMPGTX DEST, SRC X = B / W / D DEST and SRC are MMX registers When DEST is greater than SRC, set the corresponding DEST total 1, otherwise the corresponding DEST total 0 is set. Have a small trick to use the comparison instruction: PCMPEQD MM ?, mm? Clear MM? Of course, use the following PXOR MM ?, mm? 6.8 MMX instruction set logic and shift instruction PAND DEST, SRC DEST is the MMX register, SRC is MMX register / memory unit DEST = DEST & SRC. Pandn Dest, SRC DEST is the MMX register, SRC is MMX register / memory unit The role of this instruction is DEST = (~ DEST) & SRC. POR DEST, SRC DEST is the MMX register, SRC is MMX register / memory unit DEST = DEST | SRC. PXOR DEST, SRC DEST is the MMX register, SRC is MMX register / memory unit DEST = DEST ^ SRC. Note that the MMX instruction has no directives such as a PNOT. Do you think of a solution? PSLLX DEST, SRC X = W / D / Q, indicating a unit of displacement DEST is the MMX register, the SRC can be a variety of registers / memory / immediately Left shift, two examples: MM0 = 86e1 04c7 19f8 42ee PSLLW MM0, 4 after mm0 = 6e10 4c70 9f80 2ee0 MM0 = 028F 76AA 85C9 bee1 PSLLQ MM0, 8 after MM0 = 8f76 AA85 C9BE E100 PSRAX DEST, SRC X = W / D DEST is the MMX register, the SRC can be a variety of registers / memory / immediately Perform the number of symbols to right. PSRLX DEST, SRC X = W / D / Q DEST is the MMX register, the SRC can be a variety of registers / memory / immediately Perform no symbol number right shift. We will notice that there is no instruction to be used in bytes of bytes of bytes to be used in the MMX instruction set, and the method of solving the problem is also very simple. Analyze the results obtained by the word-oriented unit left / right shift and the result of the left / right movement in bytes You will find that they have similar, but because in general, our left / right shift It is fixed, so we only need to first shift in the left / right shift by the word first, and then according to the number of digits of the left / right shift. If your number of digits in left / right shift is uncertain, it can only adjust the instruction by the following format. 6.9 MMX Directive Set Format Adjustment Instruction The format adjustment instruction is a very important component of the MMX instruction, including two categories packaged and extended. Packing instructions are: Packssx DEST, SRC X = WB / DW Finally, there is a symbol, with color saturation Example: mm0 = 8000, -200, 55, 34 MM1 = -1281, 27, -99, 127 Packsswb mm0, MM1 MM0 = -128, 27, -99, 127, 127, -128, 55, 34 Packusx Dest, SRC x = WB In addition to the end of the unsigned number (0-255), it is the same as PACKSSX. The expansion instructions are: PUNPCKHX DEST, SRC X = BW / WD / DQ Example: MM0 = AF, 45, 0E, 8A, 12, 67, FF, 00 MM1 = 11, 91, AB, 5C, 93, B8, 0F, 09 PUNPCKHBW MM0, MM1 after MM1 = 11AF, 9145, AB0E, 5C8A PUNPCKLX DEST, SRC X = BW / WD / DQ Example: MM0 = AF, 45, 0E, 8A, 12, 67, FF, 00 MM1 = 11, 91, AB, 5C, 93, B8, 0F, 09 PUNPCKLBW MM0, MM1 MM0 = 9312, B867, 0FFF, 0900 The MMX instruction set has been basically introduced, and now you should think about how to apply it to the program. Some commonly used 2D special effects MMX instructions are available for reference. Chapter 7 I didn't want to know a good name If you only have a game with the knowledge telling the above chapter, people who like it may be not much? Why? Because no one will play a game that is not smooth and the sound effect is not smooth. In order to better manage various input devices in the game, we need to use DirectInput. The use of DirectX Audio can achieve a variety of more realistic music effects in the game. They are important components of DirectX. Before we use DirectInput, we need #include and add DINPUT8.LIB and DXGUID.LIB in the project, and we need #include before using DirectX Audio. DSOOND.LIB and DXGUID.LIB are added in the project. 7.1 Read keyboard data First, we must create a DirectInput8 object (DirectX 9.0 does not change much to DINPUT and DAUDIO), just like this: LPDIRECTINPUT8 PINPUT; DirectInput8Create (GetModuleHandle (NULL), DirectInput_version, IID_IDIRECTINPUT8, (void **) & pinput, NULL); Then we need to create a DirectInput device: LPDIRECTINPUTDEVICE8 PDEV; Pinput-> CreateDevice (Guid_syskeyboard, & PDEV, NULL); Set up its data format: PDEV-> setDataFormat (& C_DFDIKEYBOARD); Set its collaboration, here is set to exclusive equipment front desk: PDEV-> SetCooperativeEvelevel (hwnd, discl_exclusive | discl_Foreground); Get the device: PDEV-> ACQUIRE (); After initialization as above, we have put Windows to deprive the control of the keyboard, and the next keyboard message will not be sent to the message loop, we can take the statement that handles the keyboard message in the message loop. Of course, at this time we need to properly, for example, when you refresh the game, join the statement to read and process the keyboard data, just like a program below: #define keydown (key) (buffer [key] & 0x80) // Defines a macro to handle keyboard data Char buffer [256]; // Keyboard data PDEV-> getDeviceState (SIZEOF (BUFFER), (LPVOID) & buffer; // Get keyboard data IF (keydown) // If the XXX button is pressed ... (see Appendix 2) { ... // Handling } ... // Handling other keys Haha, it is really convenient. Sometimes it is really a little doubt if DirectX is "reverse" back to the distant cute DOS era. Because whether DirectInput or DirectDraw is too similar to DOS. 7.2 Read mouse data The steps to read the mouse data and read the keyboard data are similar, first of all, to create devices: Pinput-> CreateDevice (Guid_sysmouse, & PDEV, NULL); Set data format: PDEV-> setDataFormat; & c_dfdimouse; Set collaboration: PDEV-> SetCooperativeEvelevel (hwnd, discl_exclusive | discl_Foreground); Get the device: PDEV-> ACQUIRE (); So how do you read mouse data? If you want to get the current state of the mouse, you can: DIMOUSESTATE MOUSE_STAT; / / Mouse Status // Get a mouse state PDEV-> GetDeviceState (SizeOf (DimouState), (LPVOID) & mouse_stat); The obtained mouse_stat is a DimouState type structure, which has four members: LX, LY, LZ, and RGBB /TTONS [4]. Where LX, LY and LZ are the distance from the X-axis, the Y-axis, and the Z-axis (roller) direction since the last call, not the coordinates at this time; its distance unit is not a pixel, but you You can do it in pixels. So, we need to define two variables Mousex = 0 and mousey = 0, then add LX and LY. The benefits of doing this are that the mouse coordinates are no longer restricted by the screen, and the mousex and mousey values in the screen center can always be 0, and change with the screen resolution. RGBB /TTONS is an array that stores which mouse keys pressed, we can do this to read it: / / Define a macro to handle mouse data #define mousebuttondown (b) (mouse_stat.rgbbuttons [b] & 0x80) IF (MouseButtondown (0)) // If the left button is pressed ... { ... // Handling } ... // Treated right-click (1) and medium key (2) 7.3 Recovery and close DirectInput 7.3.1 Restore DirectInput Equipment As in DirectDraw, the DirectInput device will "lose" when using the DirectInput program. The recovery method is very simply: Turn off DirectInput and then reinitialize. 7.3.2 Turn off DirectInput Turning off DirectInput is also very simple (definition of Safe_Release): PDEV-> UNACQUIRE (); SAFE_RELEASE (PDEV); SAFE_RELEASE (PINPUT); 7.4 Initialization and close DirectX Audio 7.4.1 Initializing DirectX Audio Before using DirectX Audio, press the rules or initialize it. In this initialization program, three Dxaudio are available in this initialization program: iDirectMusicLoader8, IDirectMusicPerformance8, and iDirectMusicSegment8. IdirectMusicLoader8 As the name reflex is used to transfer music, IDirectMusicPerFormance8 can think is an audio device, and iDirectMusicSegment8 represents music. #include IdirectMusiCloader8 * ploader = null; IDirectMusicPerformance8 * pperf = null; IdirectMusicSegment8 * pseg = null; Coinitialize (NULL); // Initialization COM CocreateInstance (CLSID_DirectMusiAder, Null, CLSCTX_INPROC, IID_IDIRECTMUSICLOADER8, (void **) & ploader; // Create a PLOADER object CoCreateInstance (CLSID_DirectMusicPerformance, Null, CLSCTX_INPROC, IID_IDIRECTMUSICPERFORMANCE8, (void **) & pperf); // Create a PPERF object PPERF-> initaudio NULL, / / This can be a pointer to the idirectMusic * object Null, / / This can be a pointer to the idirectsound * object HWnd, // window handle DMUS_APATH_SHARED_STEREOPLUSREVERB, / / AUDIOPATH Type // Here, the stereo and reverberation, the effect is very good 64, // Music channel number DMUS_AUDIOF_ALL, // All features of the sound card NULL / / can point to a DMUS_AUDIOPARAMS object, more detailed, various parameters ); 7.4.2 Close DirectX Audio Turn off DXAUDIO or old ideas, first stop music at sedimentation 7.5.3, then Release can: PPERF-> Closedown (); // Close SAFE_RELEASE (PLOADER); // Release Object SAFE_RELEASE (PPERF); SAFE_RELEASE (PSEG); Couninitialize (); // Stop using COM 7.5 Playing MIDI and WAV Music MIDI music and WAV music are often used in game programming. The former is generally used as background music, and the latter is mostly used in various sound effects, such as launch missiles. Although we can use 3.4.4, using DXAUDIO, you can use hardware resources to achieve fewer CPU usage. Methods as below: 7.5.1 Turn into MIDI and WAV files Before playing music, the first step of course is to transfer music files: Char strsoundpath [max_path]; // Store the path where the music is GetCurrentDirectory (max_path, strsoundpath); // Get the path where the program is located STRCAT (STRSOUNDPATH, "// Sounds"); // Set music here Sounds subdirectory under the program path Wchar WSTRSOUNDPATH [MAX_PATH]; // Store the path in the form of Unicode / / Turn the path into a Unicode form MultibyToWideChar (CP_ACP, 0, STRSOUNDPATH, -1, WSTRSOUNDPATH, MAX_PATH); PLoAder-> setsearchdirectory Guid_directMusicalLTypes, / / Search all supported formats WSTRSOUNDPATH, False ); Wchar wstrfilename [max_path]; // Store file names in Unicode form // Transform the file name into a Unicode form MultibyToWideChar (CP_ACP, 0, "A.MID", -1, WStrFileName, Max_Path; Ploader-> LoadObjectfromfile ( CLSID_DIRECTMUSICSEGMENT, // File Type IID_IDirectMusicSegment8, // Target Object Type WSTRFILENAME, / / file name, also should be in the form of Unicode (LPVOID *) & PSEG // Target object ); 7.5.2 Playing MIDI and WAV files After transferring the music, you can start playing music when appropriate: PSEG-> setrepeats (number of music to be repeated); // DMUS_SEG_REPEAT_INFINITE is unlimited PSEG-> Download (PPERF); // Passing the music data to PPERF PPERF-> PlaySegmentex PSEG, / / Music to play Null, // can now only be NULL NULL, 0, 0, NULL, NULL, Null // Audiopath, don't worry about it now? ); 7.5.3 Stop playback It is also very simple to stop playing music: PPERF-> STOP NULL, / / Stop in which channel, NULL represents all channels NULL, 0, // How much time it stops playing 0 ); 7.6 Playing music in 3D space Our next question is how to make music more realistic, it is best to make music 3D, which will greatly strengthen authenticity. It is not difficult to achieve this, first we have to set the so-called Audiopath, which can specify the play mode of music. IdirectMusicaudiopath8 * ppath; // audiopath PPERF-> CreateStandardAudiopath (// Create Audiopath DMUS_APATH_DYNAMIC_3D, // 3D 64, // channel number True, / / Do you immediately activate Audiopath & ppath // To create Audiopath ); Then, we have to get a "3D cache" from Audiopath, which stores information such as playback of music. IDirectSound3DBuffer8 * PBuffer; // 3D cache PPath-> getObjectInpath ( DMUS_PCHANNEL_ALL, / / Search all channels DMUS_PATH_Buffer, / / Cache for DirectSound 0, GUID_NULL, 0, IID_IDIRECTSOUND3DBuffer8, // Cache Type (LPVOID *) & PBuffer // To create a cache ); The next step is to set the music where the music is played in 3D space. E.g: PBUFFER-> setPosition (-0.1f, 0.0f, 0.0f, ds3d_immediate); This instruction sets the play position of the music (-0.1f, 0.0f, 0.0f), because the default listener location is in the coordinates (0, 0, 0), the face is toward the positive direction of the Z axis, head towards the head The y-axis is in the direction, so its effect will be the audience's right ear, but the left ear can not hear the sound, just like music from its own right. If the last parameter is set to DS3D_deferred, this will be suspended until idirectSound3DListener8 :: CommitDeferRedSettings () so that multiple settings can be processed once. We can also set the speed of the music source and its playback angle: PBuffer-> setVelocity (vx, vy, vz, ds3d_immediate); // set at the X, Y, Z-axis direction / / Set the playback angle size (degrees), Inncone is the internal angle, Outcone is an angle // Music is not attenuated in the internal angle range, slowly attenuating between the internal and external angles, disappears when the outer angle is exceeded PBuffer-> SetConeangles (Inncone, Outcone, DS3D_ImMediate); PBuffer-> SetConeorientation (x, y, z, ds3d_immediate); // Set which direction play So how do we set the position of the audience? Can be like this: IDirectSound3dlistener8 * plistener; PPath-> getObjectInPath (// Create an audience 0, DMUS_PATH_PRIMARY_BUFFER, 0, GUID_NULL, 0, IID_IDIRECTSOUND3DLISTENER8, (LPVOID *) & PLISTENER ); // Set the audience facing (x1, y1, z1), head towards (x2, y2, z2) PLISTENER-> SETORIENTATION (x1, y1, z1, x2, y2, z2, ds3d_immediate); plistener-> setPosition (x, y, z, ds3d_immediate); // listening location PListener-> setVelocity (VX, VY, VZ, DS3D_IMMEDIAT); // listening speed 7.7 Playing MP3 Music The problem of MIDI music is that the dependence of the sound card is too large, the good sound card and the active playback effect are too far. WAV music is absolutely accurate enough, but the space occupied is highly visible. MP3 is probably a better solution. It is worth noting that playback MP3 does not require DirectX Audio, which is required for DirectShow. So, we want #include And add Strmiids.lib in the project. 7.7.1 Detailed into the MP3 file Let's talk about the initialization DirectShow and transfer to MP3. First, we want to define three objects, where the IGraphBuilder * type can be considered to be the media player, IMEDIACONTROL * type variable is responsible for the media playback control, and the IMEDIAPOSITION * type variable is responsible for the media's play position setting. Igraphbuilder * pgbuilder; IMediaControl * pmcontrol; IMediaPosition * pmpos; Coinitialize (NULL); // Initialization COM // Create each object CoCreateInstance (CLSID_FILTERGRAPH, NULL, CLSCTX_INPROC, IID_IGRAPHBUILDER, (Void **) & pgbuilder; PGBuilder-> QueryInterface (IID_IMEDIACONTROL, (void **) & pmcontrol); PGBuilder-> queryinterface (iid_imediaposition, (void **) & pmpos); Char strsoundpath [max_path]; // Store the path where the music is Wchar WSTRSOUNDPATH [MAX_PATH]; // Store the path in the form of Unicode GetCurrentDirectory (max_path, strsoundpath); STRCAT (STRSOUNDPATH, "// Sounds //"); STRCAT (STRSOUNDPATH, "A.mp3"); // Suppose is to play A.mp3 in the Sounds subdirectory MultibyToWideChar (CP_ACP, 0, STRSOUNDPATH, -1, WSTRSOUNDPATH, MAX_PATH); PGBuilder-> Renderfile (WstrsoundPath, null); // Turnover 7.7.2 Playing MP3 files The method of playing MP3 is very simple: PMPOS-> PUT_CURRENTPSITION (0); // Move to the file header PMControl-> run (); // Play 7.7.3 Stop playback and release objects Finally, we have to stop playing music and release each object: PMControl-> stop (); // Stop playback // Release the object SAFE_RELEASE (PMControl); SAFE_RELEASE (PMPOS); SAFE_RELEASE (PGBuilder); Couninitialize (); // Release COM Chapter VII Support the Foundation of the Game In this chapter, we will look at the application of data structure, algorithm and artificial intelligence in the game. I think everyone knows the word "artificial intelligence", but what is the data structure and algorithm? It is simple to say that the data structure is the form of various data in the program, and the algorithm is a method of processing this data. Niklaus Wirth has said "Data Structure Algorithm = Programs", it can be seen that its importance. 8.1 Link Picture The linked list is a flexible data structure that can be said to use the pointer to the extreme. The simplest linked list consists of a node like this: Struct node // node { INT data; // node data Node * next; // Pointer to the next node } A node of a linked list is connected to one by the next train compartment. When we look for data in the list, we also have to look down. You can imagine that adding a new node in any location of the linked list is very simple, and when you delete a node in the list, just point your parent node to its child node. Because of the characteristics of the linked list, it is widely applied to the number of various elements or the order of the elements often requires a change. We can also use a two-way linked list, which uses a pointer to a node. This will make the linked list more convenient - can look up nodes from the post, but also increase the size of the linked list. Link lists have many applications in game programming, such as organizational games like wizels (Sprite, referring to something that will move in the game) This kind of element often needs to be modified. 8.2 hash table Using the hash table (haveh table) can greatly reduce the time of finding work. For a simple example, if you have to find a word in a dictionary, what should you do? If you don't use a hash table, then you seem to only find one by one. Of course, we know that the dictionary is sequential, so it is a faster way to use two points. But if it is a complete disorderless data such as employee numbers? At this time, we need a hash table. How to build a hash table? The so-called hash table is actually a very simple thing, it can be said to establish an index for data. Or the above example, we should first pass the transformation of a function, turn all the words in the dictionary to some different numbers as much as possible. If you can do it completely, this function is a perfect hash function. Of course, this is obviously difficult. A worse hash function - we give it a name called f (x) - is a letter to the first word, convert the word between 0 and 25, just like the typical dictionary . A good solution is to convert all the letters of the words, then add it, and take a large number of large numbers. Next, we build a large group of HashTable, and its size can accommodate all the possible values of all hash functions, for F (x), we have to open a HashTable [26]. Then we turn every element of this array into a linked list, put the corresponding word a piece of place (in fact, it should put it in an array immediately after converting the word). At this time, the content of HashTable [0] is like this: "a"? "AACHEN"? "Aalborg"? "Aardvark"? ... Now everyone looks out, yes, we just want to convert the words we want to convert into a number through f (x), and then look up in HashTable [f (x)]. The better the hash function, the less words of the same hash function, the faster our look. However, it is impossible to ignore, the array is likely to be bigger. Therefore, the hash table is a space for time. About the selection of the hash function, and if there are two element hash functions, it is now more research. For example, there is an approach: when HashTable [f (x)] has been occupied by other elements, see if HashTable [G (F (X))] is empty, if you can't still see HashTable [G (g (g) f (x)))] until the behavior is stopped. G (x) can be X 1. Obviously, if this approach is used, the size of HashTable [] is more than the number of elements. This approach avoids the use of the linked list, but increases the cost of calculating G (x). In short, everything should be selected according to the actual situation. Finally, give everyone a better use Hash function, it can turn a word to an integer: INT hashf (const char * s) { Int hash, i, l; Hash = 0; l = strlen (s); For (i = 0; i Hash = (HV * 26 S [i] - 'a')% size; // size is the size of the Hash table Return HV; } 8.3 Quick Sort One of the most common algorithms is the sorting algorithm. Since the speed of the sort algorithm is high, we usually use rapid sorting. Its algorithm is as follows: Void Quicksort / / Regular A group A, start is the position of starting, End is a row {// sequence end position, such as A [10], start = 0, end = 9. INT P = Begin; INT Q = END; INT MID = a [P]; // Standard Int temp; While { While (a [p] While (a [q]> mid) q-; // The number of right on the array is greater than or equal to the standard IF (P <= Q) { Temp = a [p]; a [p] = a [q]; A [q] = TEMP; // Exchange A [P], A [q]. P ; Q -; } } IF (Q> Begin) Quicksort (Begin, Q); // Continue to sort the first half IF } In fact, rapid sorting ideas is not difficult to understand, first set two pointers at the end of the head and move to the middle, find that the number of exchanges referred to in the two pointers will improve the order status, such as the two pointers arrive or cross each other, then indicate The number has been divided into two groups, and then recursively call yourself to implement the same process of the two groups. 8.4 Depth Priority Search The search algorithm that is speaking, they are useful in the road and AI. The most commonly used graph search algorithm is depth priority search (DFS) and breadth priority search (BFS). Depth priority search, you can go, if there are multiple roads, you can go according to certain order selection (such as up and down), but don't go back. If there is no way to go back. Obviously this method does not necessarily find the shortest path, but it is very small for memory. Since there is similar to the real way to find the process, you can make the wizard move directly according to the search process, no waiting. However, due to the order of the upper and lower ordinals, the Elf is not going toward the shortest route, so the mobile route is not really true, especially when it is more empty, it will be easy to find the way. For example, the following map (black lattice represents the wall, other plaids can walk) If you want to walk from the upper left corner to the lower right corner, the depth priority search is as follows (A-> Z-> A-> O): ?????????????????????????????????????????????????????????? ??? ???? a ??? a ??? d? E? F ?????? ??? ???? b ??? z? B? C ??? g ????????? c? X? Y ??????? h ?????? ??? ???? d ??????????? I? J ???? ??? ???? e ??? l ????? T ??? k? L ?? ??? ???? f? J? K? N? O? S? U ??? m ?? ??? ???? g ??? m ??? p ??? v ??? n ?? ??? ???? h? I ??? r? Q ??? w ??? o ?? ??? ???????????????????????? Figure 8.1 Below is the standard depth priority search program: #include #include Using namespace std; #define SX 10 // Wide #define SY 10 // Long INT DX [4] = {0, 0, -1, 1}; // The effect of four mobile directions on X and Y coordinates INT DY [4] = {- 1, 1, 0, 0}; Char block [SY] [SX] = // Obstacle table {{0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 0, 1, 1, 1, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0}, {1, 1, 1, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 0, 0, 1, 0, 1, 1, 1, 0}, {0, 1, 0, 0, 1, 1, 1, 1, 1, 0}, {0,0,0,1,1,0,0,0,1,0}, {0, 1, 0, 0, 0, 0, 1, 0, 1, 0}, {0, 1, 1, 1, 0, 1, 1, 0, 1, 1}, {0,0,0,0,0,0,1,0,0,0}}; INT MAXACT = 4; // Total number of mobile directions Char Table [SY] [SX] = {0}; // has arrived INT level = -1; // INT LevelComplete = 0; // Does this step are completed INT Allcomplete = 0; // All search is completed Char ACT [1000] = {0}; // Move direction for each step, search 1000 steps INT x = 0, y = 0; // current X and Y coordinate INT Targetx = 9, targety = 9; // Target X and Y coordinate Void test (); Void back (); Int actok (); void main () { Table [y] = 1; // Do it has arrived While (! allcomplete) // is all searching { Level ; Levelcomplete = 0; // Search Next While (! Levelcomplete) { ACT [Level] ; // Change the direction of movement if (ACTOK ()) // Whether the moving direction is reasonable { Test (); // Test is to go to the target Levelcomplete = 1; // This step search is complete } Else { IF (ACT [Level]> Maxact) // Searched all directions Back (); // Back to the past IF (Level <0) // All search still no results Levelcomplete = allComplete = 1; // Exit } } } } Void test () { IF ((x == Targetx) && (y == targety)) // has arrived at the target { For (INT i = 0; i <= level; i ) Cout << (int) ACT [i]; // output result Levelcomplete = allComplete = 1; // Complete search } } Int Actok () { INT TX = X DX [ACT [Level] -1]; // Will go to the X coordinate INT TY = Y DY [ACT [Level] -1]; // will be the y coordinate of the point IF (ACT [Level]> Maxact) / / Direction error? Return 0; IF ((tx> = sx) || (TX <0)) // X coordinate outfit? Return 0; IF ((Ty> = SY) || (TY <0)) // Y coordinate outfit? Return 0; IF (Table [TY] [TX] == 1) // has been reached? Return 0; IF (Block [Ty] == 1) // Is there an obstacle? Return 0; X = TX; Y = Ty; // Mobile Table [y] = 1; // Do it has arrived Return 1; } Void back () { X- = DX [ACT [Level-1] -1]; Y- = DY [ACT [Level-1] -1]; // Returned to the original point Table [Y] = 0; // Clear has arrived ACT [Level] = 0; // Clear direction Level -; // Back to the first floor } The output is 22444222222244444222244, where 1 means, 2 represents, 3 represents left and 4 represents the right. 8.5 Guangsheng Priority Search It corresponds to the depth priority search for breadth priority search. The idea of this method is very simple, just search for a point you can get, search for two steps to get ... so until you find the target point. This search method obviously ensures that the shortest path is the shortest path, but the search speed is also faster, but the occupation of space is large. Please see the standard breadth priority search procedure: #include #include Using namespace std; #define SX 10 // Wide #define SY 10 // Long INT DX [4] = {0, 0, -1, 1}; // The effect of four mobile directions on X and Y coordinates INT DY [4] = {- 1, 1, 0, 0}; Char block [SY] [SX] = // Obstacle table {{0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 0, 1, 1, 1, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0}, {1, 1, 1, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 0, 0, 1, 0, 1, 1, 1, 0}, {0, 1, 0, 0, 1, 1, 1, 1, 1, 0}, {0,0,0,1,1,0,0,0,1,0}, {0, 1, 0, 0, 0, 0, 1, 0, 1, 0}, {0, 1, 1, 1, 0, 1, 1, 0, 1, 1}, {0,0,0,0,0,0,1,0,0,0}}; Int Allcomplete = 0; // All completed flags INT levelNow = 1, // Search the first few layers ACT, / / now move direction Actbefore, // The parent of the current node Maxact = 4, // Total number of mobile directions Actnow, // now node TX, TY; // Current coordinates INT Levelfoot [200] = {0}, // Each layer last node ACTHEAD [3000] = {0}; // Parent Node CHAR ALLACT [3000] = {0}; / / The moving direction of each node Char ACTX [3000] = {0}, Acty [3000] = {0}; // Press the coordinate after the node moves Char Table [SY] [SX] = {0}; // has arrived Char Targetx = 9, targety = 9; // Target point Int actok (); Int test (); Int Actok () { Tx = actX [actbefore] DX [ACT-1]; // will go to the point X coordinate TY = ACTY [ACTBEFORE] DY [ACT-1]; // will go to the y coordinate IF ((tx> = sx) || (TX <0)) // X coordinate outfit? Return 0; IF ((Ty> = SY) || (TY <0)) // Y coordinate outfit? Return 0; IF (Table [TY] [TX] == 1) // has been reached? Return 0; IF (Block [Ty] == 1) // Is there an obstacle? Return 0; Return 1; } Int test () { IF ((TX == Targetx) && (Ty == Targety)) // has arrived { Int act = actnow; While (ACT! = 0) { Cout << (int) ALLACT [ACT]; // Push all mobile directions one step forward Act = acthead [ACT]; // So outputting down } Return 1; } Return 0; } void main () { LevelNow = 1; Levelfoot [1] = 0; Levelfoot [0] = - 1; ACTX [0] = 0; Acty [0] = 0; While (! allcomplete) { LevelNow ; // Start searching for the next layer Levelfoot [LevelNow] = levelfoot [levelnow-1]; // The new layer of the end node is set to the same level as the previous layer For (Actbefore = levelfoot [levelnow-2] 1; ACTBEFORE <= levelfoot [levelnow-1]; Actbefore ) / / Extension of all of the above-storey nodes { For (ACT = 1; ACT <= Maxact; Act ) // Try all directions { IF ((ACTOK ()) && (! allcomplete)) // operation is feasible? { Levelfoot [LevelNow] ; // Mobile tail pointer ready to join the new node Actnow = levelfoot [levelnow]; / / Find Add to New Node Location Acthead [actnow] = actbefore; // set head pointer ALLACT [ACTNOW] = act; // Add new node ACTX [ACTNOW] = TX; Acty [actnow] = Ty; // Store the moving position Table [Ty] [TX] = 1; // Do it has arrived IF (Test ()) Allcomplete = 1; // Complete? } } } } } The output is 442224444422322244422, it is to be 22444222222444144222444, it is indeed a shorter path found than DFS. Use DFS and BFS to solve a wide variety of problems. Let's take a famous middle school student computer competition question to see if you can solve it faster: There are three buckets that have no scale, the capacity is 3 liters, 5 liters, 8 liters. Now the 8 liters of buckets are full, you can put the water in the bucket. For example, first 8-> 3, then there will be 5 liters of water in the 8 liters, 3 liters of buckets will be filled; then 3-> 5, then 3 liters will be empty, 5 liters will have 3 liters . Your goal is to distinguish these 8 liters of water, even if there are 4 liters of water in the 5 liters and 8 liters. 8.6 Heuristic Search Have you heard a Dongdong called a *? A * is a heuristic search method. Of course, you haven't heard it, don't matter, let's talk about the heuristic search. The core of heuristic search is an estimated function f (x) that extends the node having a small f (x) value before expanding the node. F (x) is equal to G (x) h (x), where g (x) is the price from the start state to the current state, which is generally how many steps have been searched; and H (x) is current state to The estimate of the target status, that is, how many steps are estimated to go to the target. In order to ensure that the search is the optimal solution, H (x) must be greater than or equal to the actual cost, and f (child node) must also be greater than or equal to F (parent node). For the question of finding the road, we can set H (x) as a straight line distance from the target, apparent that it can satisfy the above two conditions to ensure that the shortest path is found. The benefits of heuristic search are fast speed and take up a lot of space. Obviously, the key to this algorithm has two points: 1. How to quickly find F (x) minimal node in a large number of nodes; DFS and BFS apply to the way to find the way, relying on a array table [SY] [SX] to occupy space, but if you say it, if your search is limited to small, such as a few screens Using an array method better). First, let's take a look at the data structure you want to use: #include Using namespace std; INT node_count = 0; // currently to extension the number of nodes Int allNode_count = 0; // current node number #define SX 10 // Map Wide #define SY 10 // Map Long #define max_node 100 // Allows how much to extension node #define max_allnode 1000 // Allow node number #define max_hash 1999 // Hash table size, it is best to INT TX = 9, TY = 9; // Target coordinates INT DX [4] = {0, 0, -1, 1}; // The effect of four mobile directions on X and Y coordinates INT DY [4] = {-1, 1, 0, 0}; Char block [SY] [SX] = // Obstacle table {{0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 0, 1, 1, 1, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0}, {1, 1, 1, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 0, 0, 1, 0, 1, 1, 1, 0}, {0, 1, 0, 0, 1, 1, 1, 1, 1, 0}, {0,0,0,1,1,0,0,0,1,0}, {0, 1, 0, 0, 0, 0, 1, 0, 1, 0}, {0, 1, 1, 1, 0, 1, 1, 0, 1, 1}, {0,0,0,0,0,0,1,0,0,0}}; Struct Node // Information to be expanded { INT X, Y, F, Level, N; } Node [MAX_NODE]; Struct // node information { Int act, father; } Allnode [MAX_ALLNODE]; Struct // Hash table, used to determine if the node has accessed { INT X, Y; } Hash [MAX_HASH]; All pending nodes are stored in an array node []. Every element of Node [] is a structure node {Int x, y, f, level, n;}, where x and y are node coordinates, F is the F function value of the node, and Level is the node is located, N It is the location of the node in allNode []. Node [1] is always the smallest and unlasting node of F function value, which is implemented by addNode () and getNode (). Finally there is a node_count, store the actual number of current nodes. The actual structure of array node [] is like this: Figure 8.2 A bit like a tree, the biggest feature of this tree is that the F function value of the parent node is always small than the child node, such as Node [1] .f Node [x / 2] .f (x> 1). So how do we join a new node to maintain the nature of this tree? First, we have to put Node_Count , at this time node [node_count] (as shown in Figure 8.x is Node [10], it is the child node of Node [5]). Then, we compare this new node and the F function value of its parent node. If the new node is smaller, copy its parent node to the new node (such as Node [10] = Node [5];), the new node is moved to the parent node, so that it is done; if The parent node is smaller, the location of the child node is determined. The code is like this: Void AddNode (int X, int y, int Act, int level, int fither) { IF ((x> = sx) || (x <0) || (Y> = Sy) || (Y <0) || (Block [Y]) || (Checkhash (X, Y)) // Checkhash (x, y) Check if the node has accessed Return; Node_count ; INT P = node_count, q; INT f = level abs (x-TX) ABS (Y-TY); // Inspiration function definition While (p> 1) { Q = P >> 1; IF (f Node [P] = Node [q]; Else Break; P = q; } Node [P] .x = x; Node [p] .y y; Node [p] .f = f; Node [p] .level = level; Node [p] .n = allnode_count; Allnode [allnode_count] .act = ACT; Allnode [allnode_count] .father = father; AllNode_count ; Add2hash (x, y); // Add to haveh table } How to remove Node [1] from Node []? Directly taken out anyone, the problem is how the remaining subtiles and the child nodes are handled. However, it is not difficult. First, we find a f () smaller in both child nodes of Node [1], such as Node [2], move it to it; then two children in this child node Sub-node Node [4] and Node [5] are found to be smaller, move it to Node [2]. This has been done until the last layer. So, in the following, we can move the node [node_count] to there, and put node_count--. Node getNode () { Node Top = Node [1]; INT P, Q; Node a = node [node_count]; Node_count--; P = 1; Q = P * 2; While (q <= node_count) { IF ((Node [q 1] .f Q ; IF (Node [q] .f Node [P] = Node [q]; Else Break; P = q; Q = P * 2; } Node [P] = a; Return Top; } Next, let's take a look at how the add2hash function adds the node to the Hash table: Void Add2hash (int X, int y) { INT f = (x * Sy Y)% max_hash; // hash function definition For (;;) { IF ((Hash [f] .x) || (HASH [F] .y)) // If X and Y are 0, the Hash position is not occupied. { F ; // look at the next position if it is occupied } Else // find an unbeatable location { Hash [f] .x = x 1; // plus 1 to prevent confusion with unbeatable Hash positions Hash [f] .y = y 1; Break; } } } So how is Checkhash () work again: Int Checkhash (int x, int y) { INT f = (x * Sy Y)% max_hash; For (;;) { IF ((Hash [f] .x) || (Hash [f] .y)) { IF ((Hash [f] .x == x 1) && (hash [f] .y == y 1)) { Return 1; // found } Else F ; // find a next location } Else { Return 0; // can't find } } } Finally, let's take a look at the main program: void main () { INT level = 0; AddNode (0, 0, -1, 0); For (;;) { Node a = getNode (); // Remove a node Level = a.level 1; IF ((a.x == TX) && (a.y == TY)) / / Is it a target? Break; For (int i = 0; i <4; i ) { AddNode (A.X DX [I], A.Y DY [I], I, Level, A.N); // Extension This node } } // Start output node Allnode_count -; while (allnode [allnode_count] .act! = -1) { Cout << allnode [allnode_count] .act 1; Allnode_count = allnode [allnode_count] .father; } } Since the moving direction of the characters in the above program is limited in four directions, the path we get must be excessive transition (this is also a pity in DFS and BFS in the game. " It "straightened". A very simple straightening idea is: While (! is all straightened) { All straighted = true; For (i = 0 to node) { For (j = i to node number) { IF (I to J's straight line does not pass any obstacles) // This is a step in need optimization { Remove nodes between I and J; All straighted = false; } } } } How should I achieve this pseudo code fastest? We can scan several times first, straighten the most common curved paths so that the number of nodes will be greatly reduced, and if any method is judged, it will be unobstructed between I and J. 8.7 Dynamic Planning Dynamic planning is a very interesting algorithm. It can use extremely simple statements to complete complex tasks. Let's first look at a map (online digital representative distance): Figure 8.3 In order to find the shortest distance from the Aland to B, we can use dynamic planning, which is like this: #include Using namespace std; Const int n = 6; // How many places // The distance between the two places, if it is a large number, such as 99999, indicating that there is no road between the two places // ourselves to your own distance should be 0 INT DIS [N] [N] = {{0, 5, 8, 99999, 99999, 99999}, {5, 0, 2, 7, 99999, 99999}, {8, 2, 0, 1, 6, 99999}, {99999, 7, 1, 0, 99999, 4}, {99999, 99999, 6, 99999, 0, 3}, {99999, 99999, 99999, 4, 3, 0}}; INT D [N]; // Some place from A void main () { D [0] = 0; // a distance from the A land is 0 For (int i = 1; i { D [I] = 99999; // Except for A, the distance from A to A is estimated to be a large number. } For (i = 0; i { For (int J = 0; J { For (int K = 0; K { IF (D [J]> D [K] DIS [J] [K]) // If the distance can be improved { D [J] = D [K] DIS [J] [K]; // Improved } } } } Cout < } In fact, this program is a bit large, because we can use the same algorithm to find the shortest distance between any two points in the map. The main disadvantage of this algorithm can also see it, it is too slow. As long as the number of nodes, the triple cycle will consume a lot of time. 8.8 Neural Network I believe that everyone will lose to the computer for the first time, but it is more than the computer, and it can be easily wins after touching it. Because people have the ability to learn, the computer is not. So what to use to make your computer's thinking approach in humans, and have your ability to learn? Neural Networks is a very popular implementation method. We know that human nervous system is composed of neurons, and neural networks are no exception, but the reaction of neurons in neural networks is greatly simplified. A typical neural network is composed of any multiple input and output, which outputs equal to 1 / (1 es), s all input value * weights (each input value has a corresponding weight, stay It is the value of the weight when training is trained. For example, if the input of a neuron is: x1 = 0.5, X2 = 0.8, X3 = 0; the corresponding weight is: w1 = 0.1, W2 = 0, W3 = 1, then S = 0.5 * 0.1 0.8 * 0 0 * 1 = 0.05, the output of neurons is 1 / (1 E-0.05) = 0.512. Complicated networks can be constructed with multiple neurons, just like this: Figure 8.4 If we give X1 input 1, X2 input 0, then the neurons located on the upper left will output 0.881, the neurons located in the upper left will output 0.500, the two results and 1 input the last neurons, the final result f is 0.655. Let's take a look at how to train the network. Or the above example, if we want to use this network to implement parity (if X1 and x2 are 0 or 1, then f = 1; otherwise, if X1 and X2 are 0 one is 1, f = 0), Then we can train this network like this: 1 Calculate the error δ of the last neuron, which is equal to (D-F) * f * (1-f), where D is the desired output, f is the actual output. In this example, δ = (0-0.655) * 0.655 * (1-0.655) = - 0.148. 2 Push the error of each neuron of a layer of network. For example, there is only one output terminal in the upper left corner, we must first calculate the δ of the neuron of neurons to this path (if there are multiple outputs, putting this product of all the output), Here is -0.148 * 3 = -0.444. The output of this neuron is 0.881, then we will obtain -0.444 * 0.881 * (1-0.881) to obtain δ of this neuron is -0.047. The same reason, δ of the upper left corner neuron is 0.074. 3 For each path, its new W = W ΔX, δ is δ of the neuron directed to this path, X is the input value of this path. Thus, the upper left corner god donate w becomes 1.953, -2 and -0.047; the left lower corner neuron w becomes 1.074, 3 and -0.926; the last neuron w changes to 2.870, -2.074 and - 1.148. If we enter the data you just now, it will find that F be changed to 0.563, which is indeed a more receiving value of 0.655 before the training. 8.9 Genetic Planning Genetic Programming is a geniician idea, it is inspired by evolution, and the self-evolution of algorithm is achieved by the method of "overproductive, survival struggle, genetic and variation" like natural organisms. Or use an actual problem to illustrate the method of GP: How to automatically find the neighborhood wall and walk around the wall? Here we assume that the world of the elves is two-dimensional, and it is a graphic, ethics, eight sensors: N, N, E, SE, S, SW, W, NW, when its corresponding direction cannot walk when the sensor Back 1 Otherwise, returning 0, the elves have four instructions: North, EAST, South, WEST, and the representative moving one in the corresponding direction. Then this program can realize the features we want: Figure 8.5 The meaning of the IF operator is that if the result of the leftmost branch is 1, the left branch is executed, otherwise the rightmost branch is performed. In fact, you can define other operators and instructions to write any programs into this format. The current problem is how to find such a program with GP method? The first step is to randomly generate a large number of applets, and then begin optimize them, you can freely choose the method, a typical method is: 1 Randomly select 7 programs, which is the best to test, add it to the next generation. This is repeated until 10% of the program is copied. 2 The remaining 90% of the next generation is formed by "hybrid". The parent of the program is obtained by performing selection 1 again, and the method of hybridization is to randomly select a hybrid point in the parent program, and then exchange the entire branch below the hybrid point, the parent program or the mother program after switching. generation. 3 Sometimes "gene mutation" can be performed, and the method is to replace a branch of a branch of the 7-choice 1 to which the randomly generated new branch is replaced. The probability of mutations can be set to 1%, in fact, the probability of mutation in nature is much smaller. Chapter 9 is moving towards the three-dimensional world In DirectX 8.0, Microsoft combines the original DirectDraw and Direct3D into DirectX Graphics (but can still use these old parts), and make many modifications make it stronger and easy to use. DirectX 9.0 has made many improvements on the DirectX 8.0. With DirectX Graphics, we can make full use of hardware resources to write a beautiful 3D game. 9.1 Overview We all know that in order to determine a position in the space, we need to use three coordinates. In Dxgraphics, the extension direction of the three coordinate axes is shown in Figure 9.1. We also know that the objects in the current 3D game are actually constructed by a triangle with a map. In fact, 3D objects in Dxgraphics can also be composed of one point or one line (but don't, do this called 3D object ??). The lines and triangles are constituted by vertices (Vertex). Whether it is point, the line is still a triangle, is called Primitive in Dxgraphics. Let's talk about a concept: Texture, I think most people know what this is. Figure 9.1 The 3D object that directly created is very unreal, because each triangle of each triangle is a color, not something in daily life. The texture is like a triangular skin, which gives it no monotonous colors. Figure 9.2 We will find that the original 3D object is white, but there is an object of other colors in the world. In order to indicate that the object appears in light irradiation, we also need to use the material. People who have used 3dmax know that the material of an object is determined by four values, and they are: 1DIFFUSE The object is diffused in ordinary light. 2Ambient The color shown in the ambient light is illuminated. 3Specular The color of the highlight region of the object. 4emissive If the object is emitted, the color of the light is emitted. There is also an important concept: Mipmap. In a 3D game, some objects are far from us, and some objects are close to us, and the same texture will be displayed different (nonsense) as the distance is different (nonsense). When the object is far from us, we don't need to use such a large texture, which can increase the point display speed and make the image look more comfortable, this is MIPMAP technology (it seems a little reasonable). A consequences using this technique are also larger for memory or memory, because there is a need to store several different textures. A MIPMAP sequence is composed of a series of seminated textures, just like Figure 9.3: Figure 9.3 Finally, let's take a look at the main proxidation of the game using DXGraphics: Main () { Initialization variables and pointers; Initialization window; Initialize DirectX Graphics; Set the scene; Create a scene; For (;;) // main loop { IF (have a message) { IF (for exiting information) { Release DirectX Graphics; Release the pointer; drop out; } IF (input to users) { deal with; } Else { Call the default message handler; } } ELSE IF (program activation) { Clear scenario; (clear) Start the scene; (Beginscene) Rendering scene; End the scene; (Endscene) Display scene; (Present) If the device is lost, restore; Refresh the other parts of the game; } Else { Waiting for the message; } } } The basic concept will talk about it first, let's take a look at how to initialize DirectX Graphics. 9.2 Basic knowledge 9.2.1 Initializing DXGraphics Just like initialization DirectDraw, before initializing DirectX Graphics, we must do some preparations first: including before the program begins , The commonly used header files and add "D3D9.LIB" and "D3DX9.LIB" in the project. Then we can perform initialization as procedures below: #define _fullscreen // You can easily set the program's mode of operation - the window, or full screen INTITGRAPH (HWND HWND) // Initialization DirectX Graphics requires window handle { LPDIRECT3D9 PD3D; // D3D object LPDIRECT3DDEVICE9 PDEV; // D3D device D3DDISPLAYMODE D3DDM; / / D3D display mode D3DPresent_Parameters D3DPP; // D3D image display method IF (NULL == (PD3D = Direct3Dcreate9 (D3D_SDK_Version))))))))) Return -1; // Create a D3D object, D3D_SDK_VERSION is the version number defined in D3D9.H IF (failed (pd3d-> getadapterdisplaymode (D3DADAPTER_DEFAULT, & D3DDM))) Return -1; // Get the current display mode ZeromeMory (& D3DPP, SIZEOF (D3DPP)); // Empty D3DPP, prepare to fill content #if Defined // If you need a program to run in window mode ... D3DPP.Windowed = true; // Setting window mode D3DPP.SWAPEFFECT = D3DSWAPEFFECT_DISCARD; // Setting the change page to discard the background cache D3DPP.BACKBUFFERFORMAT = D3DFMT_UNKNOWN; / / Set the background cache format is unknown #ENDIF #if defined (_fullscreen) // If the program is required to run in full screen mode ... D3dpp.windowed = false; D3DP.HDEVICEWINDOW = hWnd; // window handle D3DPP.SWAPEFFECT = D3DSWAPEFFECT_FLIP; // Setting up the page effect is page D3DPP.BACKBUFFERCOUNT = 2; // There are 2 background cache D3DPP.BACKBufferWidth = 800; // screen width is 800 pixels D3DPP.BACKBUFFERHEIGHT = 600; // Screen length is 600 pixels // Use the refresh rate in this display mode of this display mode D3dpp.fullscreen_refreshrateinhz = D3DPRest_Rate_Default; / / Immediately display the refresh image D3DPP.Fullscreen_PresentationInterval = D3DPRest_Interval_immediate; #ENDIF D3dpp.backbufferformat = D3DDM.FORMAT; // Color depth of the desktop / / Turn on the automatic depth cache, which is automatically displayed on the correct cover relationship. D3dpp.enableautodepthstencil = true; D3DPP.AUTODEPTHSTENCILFORMAT = D3DFMT_D16; // 16-bit automatic depth cache // Create a D3D device IF (Failed (PD3D-> CreateDevice (D3DADAPTER_DEFAULT, // Using Main Display Devices D3DDEVTYPE_HAL, // use 3D hardware, if // d3ddevType_ref only uses CPU HWnd, // window handle // The following parameters use hardware T & L using hardware T & L for D3DCREATE_HARDWARE_VERTEXPROCESSING D3DCREATE_SOFTWARE_VERTEXPROCESSING, & D3DPP, // Filled above D3D Image Display Method & PDEV))) // To create D3D devices Return -1; } 9.2.2 Close DXGraphics Turning off DirectX Graphics is very simple, using 4.8 SAFE_RELEASE to release D3D objects and D3D devices. 9.2.3 Recover DXGRAPHICS Equipment Many devices in DirectX exist "loss" phenomenon (such as when you press Alt Tab to switch out DirectX programs), DirectX Graphics devices are no exception. A reset () function is provided in DirectX9 to help us recover your device, and the usage is as follows: // present is introduced at the 9.5 session, this sentence means: if the device is lost ... IF (PDEV-> Present (NULL, NULL, NULL, NULL) == D3DERR_DEVICELOST) { // If you can already use the reset () recovery device ... IF (PDEV-> TestcoOPERATIVELEVEL () == D3DERR_DEVICENOTRESET) { PDEV-> RESET (& D3DP); // D3DPP is an image display method set when the front initialization Reset rendering status; // see section 9.3.1 Reset matrix; // see section 9.3.2 } } 9.3 Setting the scene 9.3.1 Setting the rendering status The rendering status that can be set in DxGraphics is much larger, there are dozens of large and small, but there are not many commonly used. The way to set the rendering status is iDirect3ddevice9 :: setrenderstate (D3DrenderstateTate (DWord Value); where State is to set, value is to set to The following table lists the commonly used rendering status: Table 9.1 State Value Meaning D3DRS_SHADEMODE D3DSHADE_FLAT flat light pattern D3DSHADE_GOURAUD smoother light pattern D3DRS_Lighting True Opens Light False close light D3DRS_AMBIENT ARGB format Ambient light color, such as 0xFF00FF00 is green D3DRS_CULLMODE D3DCULL_NONE shows all triangles D3DCULL_CW does not display triangles arranged in three vertices D3DCULL_CCW does not show triangles arranged in counterclockwise D3DRS_FILLMODE D3DFILL_POINT only displays the vertices D3DFILL_WIREFRAME only displays a frame composed of lines D3DFILL_SOLID is displayed D3DRS_ZENABLE D3DZB_TRUE Opens the Z Cache D3DZB_FALSE Turn off depth cache You don't have to reset a rendering state each time, because it will slow down the speed, and you will never change it yourself after you set it. The default value of D3DRS_SHADEMODE is D3DShade_gouraud, and the light effect of the object in this mode is OK. If the D3DShade_FLAT effect will be bad, because one of the objects of the object can only be a color. To set up D3DRS_CULLMODE, simultaneously create a 3D object, which can achieve the blocked face is not rendered, thereby reducing approximately half display workload. However, setting these things is quite trouble, so we generally use D3DCULL_NONE. 9.3.2 Setting Matrix I don't know if you have heard the mathematical noun of "Matrix", in 3D graphics program we have to use matrices to convert the coordinates of 3D objects. You can see the most commonly used 4th-order matrices as 16 arrays of 4X4, just like this: Figure 9.3 A 4x4 matrix can multiply the coordinates (X, Y, Z) in a space, resulting in a new coordinate (x ', y', z '). The algorithm is like this: X '= (x * m11) (Y * m21) (z * m31) M41 Y '= (x * m12) (Y * M22) (z * m32) M42 Z '= (x * m13) (Y * m23) (z * m33) M43 In DXGraphics, a point in the space should be displayed in turn over three matrices to display on the screen correctly, and the three matrices are World, View and Projection, respectively. These three matrices can make three dimensions, rotate, and confuse, but generally tell the world matrix to put the shape in the correct position in the scene, the View matrix is responsible for adjusting the body according to the observer's position and the direction of view, and Projection The matrix is responsible for setting the angle of view and perspective (making the object near). If you want to manually fill these matrices, it is too much trouble, DXGraphics, of course, provides a function that makes us created these matrices, let's take a look at the example: D3DXMATRIX MATWORLD; / / Define World Matrix D3DXMAMATRIXIDENTITY (& MatWorld); // Defines its unit matrix, ie no change scene PDEV-> SetTransform (D3DTS_WORLD, & MATWORLD); // Set Matrix D3DXMATRIX MATVIEW; / / Defining View Matrix D3DXMAMATRIXLOKATLH (& matview, & D3DXVECTOR3 (X, Y, Z), where the eye is located & D3DXVECTOR3 (OX, OY, OZ), // Where is the eye looks & D3DXVECTOR3 (0.0F, 1.0F, 0.0F)); // What is "on" PDEV-> SetTransform (D3DTS_VIEW, & MATVIEW); // Setting Matrix D3DXMATRIX MATPROJ; / / Define Projection Matrix D3DXMatrixPerspectiveFovlh (& matproj, D3DX_PI / 2, // The viewing angle is magnified, set to π / 2 curvature, namely 90o 1.0F, // aspect ratio 0.01f, // eye can see more 500.0f); // eye can see how far PDEV-> SetTransform (D3DTS_PROJECTION, & MATPROJ); // Setting Matrix Need to explain the meaning of the last parameter of the D3DXMatrixlookatlh function, you can understand it this: If we stand at the original point of the coordinate, then it is to indicate which one of our heads. In general, our head is in the Y-axis direction, so it is generally set (0, 1, 0). 9.4 Creating a scene After setting the scene, we need to create a scene for display. This step has two implementation methods, the first is to tell DXgraphics all points, lines and triangles, and the second is to use the function provided by DXGraphics directly to 3D scenes such as 3DMAX. Obviously, the second method is easier, but if we have to draw a 2D thing on the screen, what should I do when I am in the star? At this time, you can use the ID3DXSPRITE object like 9.4.2. 9.4.1 Turning into 3D scene Let's tell you a bad news: DXGraphics can only transfer it to its own defined file! Next is a good news: You can go to http://www.milkshape3d.com to download a super-use Milkshape 3D, which can not only achieve files generated by various 3D Moduk software into .x format, but also 3D file formats used in Quake2, Quake3, Counter Strike, Half-Life, Max Payne, Unreal, Serious Sam, The SIMS, etc. can be transferred to .x files! However, there is a bad news: Milkshape 3D is not registered to use only one month, and the Dafa, which we often use is not good. So what do you do? In fact, there is a plugin in the DirectX SDK, you can output the scene of 3Dmax or Maya to .x, find DirectX SDK Extras on Microsoft's website. Turn on the topic: How to transfer the .x file? In fact, it is very simple: LPD3DXMESH MESH; // Mesh is the meaning of the network, here is 3D scene DWORD NMATER; // How many matials in the scene in the scene, ie material D3DMATERIAL9 * MATER; / / array of data in various materials in the scenario LPDIRECT3DTEXTURE9 * TEX; // A number of data of various textures in the scene LPD3DXBUFFER PBUFFER; // Temporary Cache D3DXMATERIAL * TMATER; // Temporary material D3DXLoadMeshfromx ("mp5.x", //.x file name D3DXMESH_MANAGED, / / Description Mesh Managed by DXGraphics PDEV, // Just created D3D device NULL, / / This can be an LPD3DXBUFFER object, saving each in the scene // Different triangles with which three triangles & pbuffer, & nmater, & mesh); // The group of things just defined above TMATER = (D3DXMATERIAL *) PBuffer-> getBufferPointer (); // Get the material of the material Mater = new d3dmaterial8 [nmater]; TEX = new lpdirect3dtexture8 [nmater]; For (DWORD I = 0; I { Mater [i] = tmater [i] .matd3d; // Tmater's role is to prevent continually calling getBufferPointer () Mater [i] .ambient = mat [i] .diffuse; // Set good Ambient material, because of one in our scene // only have ambient light, and the object used as the picture software is generally // No this material is set, the result will not see something // Create texture / / Please ensure that every triangle in the scene has textures, otherwise it will be wrong! D3DXCREATEXTUREFROMFILE (PDEV, TMATER [i] .ptexturefilename, & tex [i]); } // Release the temporary object PBuffer-> Release (); D3DXComputenormals (Mesh); // Calculate the orientation of each vertex, prepare for future use After transferring in the scene, we can perform ID3DXMESH :: OptimizeInplace () to optimize the scene, which increases many display speeds. First, we have to open a number of connections in the Mesh in Mesh (that is, the meaning of the fourth parameter of D3DXLoadMeshFromx (): DWORD * PADJ = New DWORD [MESH-> getnumfaces () * 3]; get data: Mesh-> GenerateAdjacency (0.0f, Padj); Then you can optimize Mesh: Mesh-> OptimizeInplace (D3DxMeshopt_vertexcache, padj, null, null, null); 9.4.2 Turn to 2D Image Even in 3D games, there is still a 2D thing. In fact, some seem to be 3D things in many games, such as what is the tree, in fact, is a 2D image. The easiest way to transfer and display 2D images is to use ID3DXSPRITE objects. First, we have to transfer the image as a texture into memory: LPDIRECT3DTEXTURE9 TEX; / / Pointer to the texture LPD3DXSPRITE SPR; / / Pointer to the ID3DXSPRITE object D3DXCREATEXTUREFROMFILEEX (PDEV, "cross.png", // file name D3DX_DEFAULT, // Filewide, here is automatic D3DX_DEFAULT, // High file, here is set to automatic D3DX_DEFAULT, // How much MIPMAP needs to be required, here is automatic 0, // Use of this texture D3DFMT_UNKNOWN, // Automatically detect file format D3DPOOL_MANAGED, // Manage DXGraphics D3DX_DEFAULT, / / Texture filtration method, here is set as default D3DX_DEFAULT, / / MIPMAP Texture Filtering Method, here is the default method 0xffffffff, / / transparent color color, argb format, here is white NULL, // read the image format stored in the variable NULL, // Read the palette stored in the variable & tex); // To create the texture Then you can create an ID3DXSPRITE object: D3DXCREATESPRITE (PDEV, & SPR); 9.5 Refreshing Scene In DXGraphics, you need five steps for a scene. But don't worry, in addition to rendering the scene, other steps are very simple. First, we must clear the scene: PDEV-> Clear (0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB (0, 0, 0), 1.0F, 0); This command can only clear the screen content in the specified rectangle, the meaning of the first parameter is to clear the contents of how many rectangles, and the second parameter points to a array filled with these rectangles. The third parameter description To clear those content, including rendering scenes (D3DCLEAR_TARGET), Z Buffer (D3DCLEAR_ZBUFFER) and STENCIL Buffer (D3DCLEAR_STENCIL). Fourth, 5, and six parameters respectively indicate that the scene is to be cleared, and z buffer is checked why value, and Stencil is to be cleared why value. But what is Z Buffer and Stencil Buffer? Do you remember the so-called automatic depth cache that we open in Section 9.2? That is z buffer, it is actually an array, which stores the object from the observer's eyes: 0.0F is recent, 1.0f is the farthest. We all know that near things can block far things (unless it is transparent?), But the previous display card does not understand this truth, everything must be calculated. Now, most of the display cards support this feature, no weird images (what? Do you like the previous way? Faint). In order to realize the point that is not blocked, a region is opened in the z buffer, which is STENCIL BUFFER, and the store is displayed. For example, the 8-bit Steencil Buffer is placed in 32-bit Z buffers. So by modifying Steencil Buffer, we can control which points to display, implement the hollow, simple fade out (change the color or black of an image by one point). After cleaning the clean scene, we should call IDirect3ddevice9 :: beginscene () to start the scene to prepare for the rendering scene. This function does not have any parameters, very simple. The next step should be the rendering scene, because there are several implementation methods, it is more complicated, so I put it in a 9.6 knot, and I will jump in it first. Rendering the scene should call IDirect3dDevice9 :: EndScene () end the scene, which is also a function without a parameter. Finally, we can call the idirect3ddevice9 :: present () function to display the scene. The prototype of this function is like this: HRESULT PRESENT (Const Rect * PsourceRect, Const Rect * PDESTRECT, HWND HDestWindowoverride, Const rgndata * PDIRTYREGION; In general, its four parameters should be set to NULL, that is, PDEV-> Present (NULL, NULL, NULL, NULL); This function returns D3DERR_DEVICELOST when the device is lost. 9.6 Rendering Scene 9.6.1 Rendering 3D Scene For 3D objects that are tested in 9.4.1, we can rendering: For (unsigned long i = 0; i { PDEV-> setMaterial (& mat [i]); // Set the material PDEV-> setTexture (0, Tex [i]); // Setting the texture Mesh-> DrawSubset (i); // Rendering is a triangle of the i kind } 9.6.2 Rendering 2D Image For 2D images that are transferred in 9.4.2, we can rendering: Spr-> DRAW (Tex, // Created texture Null, // Source matrix, just like DirectDraw NULL, // A D3DXVector2 structure indicates how many times the horizontal direction is expanded. Null, // A D3DXVECTOR2 structure, rotation around 0, // How many radians are rotated clockwise & D3DXVECTOR2 (397, 297), where to put it on the screen 0xfffffffff); // Image of the color, Argb format, here is opaque white // If set to 0x7ffffff, the number of 10.2 can be used to achieve translucent 9.7 Changing the scene Everyone may notice that the location and direction of the 3D object rendered, the size, etc. seem to be uncontrollable. However, the method always has, we can complete the change of 3D objects with the WORLD matrix. The method is very simple: D3DXMAMATRIX MATW, MATT, MATO PDEV-> GetTransform (D3DTS_WORLD, & MATW); // Get the original World Matrix Mato = Matw; D3DXMatrixTranslation (& Matt, X, Y, Z); // Turn Matt into a matrix that can move objects (X, Y, Z) D3DXMAMATRIXMULTIPLY (& Matw, & matt, & matw); // matw = matt * matw, you can press Matt / / Move X, Y, Z in the X / Y / Z axis direction D3DXMAMATRIXROTIONYAWPITCHROLL (& Matt, Ry, Rx, Rz); // Turn Matt into a rotation of objects / / (RX, RY, RZ) angle matrix D3DXMAMATRIXMULTIPLY (& Matw, & matt, & matw); // matw = matt * matw, you can press Matt / / RX, Ry, Rz in the X / Y / Z axis direction, respectively D3DXMatrixScaling (& Matt, SX, Sy, SZ); // Turn Matt into a matrix that can scales objects (SX, SY, SZ) D3DXMAMATRIXMULTIPLY (& Matw, & matt, & matw); // matw = matt * matw, you can press Matt / / Zoom SX, Sy, SZ times in the X / Y / Z axis direction, respectively PDEV-> SetTransform (D3DTS_WORLD, & MATW); // Setting Matrix For (unsigned long i = 0; i { PDEV-> SetMaterial (& mat [i]); PDEV-> SetTexture (0, Tex [i]); Mesh-> Drawsubset (i); } PDEV-> Settransform (D3DTS_WORLD, & MATO); // You can push the second object when you go to draw the second object, first change the WORLD matrix and restore it. Another important issue is how to achieve roaming in a 3D scene, which is also very simple, as long as the view matrix is set before each refresh according to the protagonist. 9.8 Show text Do you still remember how we use HDC to implement text output in DirectDraw? In Dxgraphics, you should have an HDC approach, we should use the ID3DXFont class. First we have to create a font: HFONT FONT; Font = CreateFont (20, 0, 0, 0, fw_bold, 0, 0, 0, 0, 0, 0, 0, 0, "taHoma"); Here we create a bold Tahoma font size of 20 pixels, the first, fifth and last parameters of CreateFont are the most worth paying attention. After creating the font, the next step should be based on it created an ID3DXFONT object: LPD3DXFONT PFONT; D3DXCREATEFONT (PDEV, FONT, & PFONT); Connect, to determine where to put the text in any location with a rectangle: MakeRect (650, 0, 800, 20); // MakeRect definitions in Section 4.6 Finally, you can output text: PFONT-> DrawText ("Hello!", // To output text Strlen ("Hello!"), // Text length & Rect, / / Created above the rectangle DT_Right, // The text is displayed in the rectangle, but also DT_LEFT or DT_CENTER, etc. D3DCOLOR_ARGB (128, 255, 0)); // The color of the image is set to translucent red 9.9 Program Example Chapter 10 I don't want to know a good name. The 3D programming knowledge described in Chapter 9 can be said to be more basic, how can we make our game more realistic and gorgeous? This requires us to use a series of advanced technologies. But rest assured, they are not very complicated. If you can master these technologies well, make a game like Quakeiii will be completely possible. Originally prepared, I finished this chapter, but my kitten didn't have a complete DX9SDK, wait until you have the difference between and DX8 and improve it ... 10.1 lighting There are three methods that are commonly used in 3D scenes. The first is to simply complete less accurate illumination effects with DxGraphics, the second method is to use the LightMap technology, the third method is to use Pixel Shader. . We still look at the simplest first in the first section. Dxgraphics provides three light sources: Point light source POINT, parallel light source directional (eg, sunlight) and spotlight SPOT (Diabloii can be seen as this light around the protagonist). A light source can be represented by a D3DLight9 structure that the parameters of this structure are: TYPE: Indicates the type of light source. Can be D3DLight_Point, D3DLight_Spot or D3DLight_Directional. Diffuse / Specular / Ambient: The color of the three lights emitted from the light source, each is a D3DColorValue structure, with A / R / G / B four members, and the value range is 0.0F to 1.0f. Position: The location of the light source is meaningless for this value of parallel light. Direction: The direction of the light source, which is meaningless for this value of the point light. RANGE: How far is the light source, the maximum allowable value is SQRT (flt_max), which is meaningless for this value of parallel light. Attenuation0 / Attenuation1 / Attenuation2: Specifies the decay method of the light. In general, Attenuation0 and Attenuation2 are set to 0, Attenuation1 is set to a constant. If you set Attenuation0 to 1 and set the rest of the two values to 0, the light will not attenuate as the distance. This value is meaningless for parallel light sources. The next three parameters are unique to spotlights. Let's take a look at what the spotlights in Diablo II are: Figure 10.1 Everyone will note that the brightness of the inner circle of the spotlight is not attenuated, and the inner ring is between the outer ring. The following three parameters are related to these: The Theta: The angle of the inner ring (the unit is curved). PHI: The angle of the outer ring (the unit is curved). Falloff: The attenuation rate of the brightness between the inner ring and the outer ring is generally set to 1.0F. After filling the D3DLIGHT9 structure, the next step is to set the light. Suppose the name of the variable of the D3DLight9 structure you just filled is D3DLight, then do this: PDEV-> setLight (0, & D3DLight); // Set to 0 light PDEV-> LIGHTENABLE (0, TRUE); // Opened the 0 light Finally, the setting is completed in the rendering state. Note that the light does not work (obviously) for 2D images, so how to achieve 2D light like Diabloii? The easiest way is to draw a 2D plane with 3Dmax and then put it as a 3D scene, but this is obviously too wasteful, and the translation and texture mixing introduced below is a good idea. 10.2 translucent The translucent is very beautiful, and now the game is very frequent. If the effect you want is to make 2D images and words translucent words, it is unbelievable to implement: PDEV-> Setrenderstate (D3DRS_ALPHABLENDENABLE, TRUE); PDEV-> SetRenderstate (D3DRS_Destblend, D3dblend_invsrcalpha); PDEV-> Setrenderstate (D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); Yes, set this three sentences. Of course, don't forget to render 2D images and text, set the color Alpha value for the desired transparency - if your image is not already included in the Alpha channel. Think about how many complicated code do we have to write in DirectDraw, it is true? So what should we do if we want to make 3D objects? It is equally simple. First, make sure you need the same Alpha value of the entire surface (eg, the effect of water) or the use of the textured Alpha channel. If it is the first, as long as the A value of this Diffuse color is set to transparency when designing the scene, then execute the following statement before rendering: PDEV-> setTexturestageState (0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); PDEV-> SettexturestageState (0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); If you want to use the second implementation method, you don't have to perform any statements, because the system default status is to use the Alpha channel. But if you have implemented the two statements to Diffuse.a, then restore it before rendering this texture: PDEV-> SetTexturestageState (0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); Haha, it seems that it is very simple to make a semi-transparent effect, no wonder everyone likes it so much? 10.3 Texture Mixed Texture mixing allows us to easily make light, brackets, etc. The principle is to give a triangle, two textures, and the mixing method of these two textures is specified by you. First, we have to transfer the second texture into memory: D3DXCREATEXTUREFROMFILE (PDEV, "Light.BMP", & Tex); Then set it to 1 texture (default texture is 0): PDEV-> SetTexture (1, tex); Set how it is mixed with the No. 0 texture: // Set the image of the texture mix source 1 PDEV-> SetTexturestageState (1, D3DTSS_COLORAR1, D3DTA_TEXTURE); // Set the texture mix source 2 is the current image PDEV-> SetTexturestageState (1, D3DTSS_COLORARG2, D3DTA_CURRENT); / / Set the texture mixing instruction to multiply PDEV-> SetTexturestageState (1, D3DTSS_COLOP, D3DTOP_MODULATE); There are many kinds of texture mixing instructions, and the following table lists several commonly used instructions: Table 10.1 D3DTOP_DISABLE prohibits texture mix D3DTOP_SELECTARG1 only shows arg1 D3DTOP_SELECTARG2 only shows Arg2 D3DTOP_MODULATE ARG1 * ARG2 D3DTOP_MODULATE2X Arg1 * arg2 * 2 D3DTOP_MODULATE4X Arg1 * Arg2 * 4 D3DTOP_ADD ARG1 ARG2 D3DTOP_ADDSIGNED ARG1 ARG2 - 0.5 D3DTOP_ADDSIGNED2X (Arg1 Arg2 - 0.5) * 2 D3DTOP_SUBTRACT ARG1 - ARG2 D3DTOP_ADDSMOOTH ARG1 ARG2 - Arg1 * arg2 In order to display the second texture correctly, we want to set the texture coordinates of the second texture. What is texture coordinates? In fact, a vertex in the Mesh corresponds to which point in the texture. For example, if a triangle is to display the half of the texture of the texture, then we can set the texture coordinates of a vertex of the triangle (0.0F, 1.0F), the lower left corner of the texture; second vertex The texture coordinate is (1.0F, 1.0F), that is, the lower right corner of the texture; the texture coordinate of the third vertex is set (1.0F, 0.0F), that is, the upper right corner of the texture. Since Mesh drawn in 3D Modules, only one set of texture coordinates, we must first convert it to have two sets of texture coordinates: Mesh-> CloneMeshfvf (d3dxmesh_managed, D3DFVF_XYZ | // Coordinate information D3DFVF_NORMAL | // Have a vertex heading information D3DFVF_DIFFUSE | // has Diffuse Color Information D3DFVF_TEX2 // has two sets of texture coordinates , PDEV, & Mesh; Then define a vertex data type according to the settings we just: Strucet Vertex { Float x, y, z; float nx, ny, nz; D3DCOLOR DIFFUSE; Float Tu1, TV1; // First set texture coordinates FLOAT TU2, TV2; // Second set of texture coordinates } Vertex * v; // Store information on the top of Mesh The next step is to lock MESH and get the vertex information: INT count = MESH-> getnumvertices (); // How many vertices in Mesh Mesh-> LockvertexBuffer (NULL, (Byte **) & V); / / The second set of texture coordinates by copying the first set of texture coordinates below / / You can change this algorithm any // For example, only the texture coordinates for several triangles For (i = 0; i { // In fact, you can change the information of each of Mesh in any Mesh. v [i] .tu2 = v [i] .tu1; v [i] .tv2 = v [i] .tv1; } Finally, I want to unlock mesh: Mesh-> unlockvertexbuffer (); 10.4 fog Fog is a more realistic effect that enables the scene, and has an application in many games. Figure 10.2 Fog effect There are two fogs in DirectX: Vertex Fog and Pixel Fog, as the name suggestions, they are based on vertices and pixels. Therefore, the effect of Pixel Fog is obviously better, and the speed of the two is not different. Let's take a look at the setting method of Pixel Fog: FLOAT FOGSTART = 0.01F, fogend = 50.0f; // fog start range and end range PDEV-> Setrenderstate (D3DRS_FOGENABLE, TRUE); // Open the fog PDEV-> setrenderstate (D3DRS_FOGCOLOR, 0x00000000); // Set the color of the fog // Set the fog's decay mode is linear PDEV-> SetRenderstate (D3DRS_FOGTABLEMODE, D3DFOG_LINEAR); // Set the beginning of the fog and the end range // * ((DWORD *) ((& x)) The role is to convert the pointer pointing to the x to point to DWORD type // The purpose of turning X to DWORD type (bypass ...) PDEV-> Setrenderstate (D3DRS_FOGSTART, * ((DWORD *)))); PDEV-> setrenderstate (D3DRS_FOGEND, * ((DWORD *))); 10.5 Confucius stickers and environment map Confucing stickers and environment maps are very important for rendering more real objects, and the method of achieving them is not complicated. Let's take a look at the implementation of the embossed stickers. Figure 10.3 Concluded map Let us look at the environment map. Figure 10.4 Environment map 10.6 particle system Use the particle system to make a variety of dazzling special effects such as flames, waterfalls. Figure 10.5 Particle System 10.7 Skeleton Animation If you have played CS, you may have a more real movement to this game, and there is a lot of guessing. In fact, it uses skeletal animation technology. This technology is to do, but it is more troublesome? Let's first take a look at what is bone animation technology. Before there is no skeletal animation technology, the three-dimensional animation is implemented using a key frame technology - ie only saved several key menh, and then achieves animation through the interpolation between them, just like the following: Figure 10.6 Key Frame Technology This is undoubtedly a very good technology, but for speed considers, interpolation is generally the simplest and rough linear interpolation. If the animation is complex, the effect will not be satisfactory. Just take the picture to do the example, we use linear interpolation will only get the effect of Figure 2, not the smooth rotation of Figure 1. Figure 10.7 What should I do if the keyframe technology problem? The various actions of Lenovo have been implemented. We can give mesh to bones, and the triangle is like the skin, will follow the bones together - actually use the bone conversion matrix to convert each vertex. So remember that the bone itself is meaningless, only the skeleton's conversion matrix is important. Figure 10.8 Skeleton Schematic Think now, if we raise the upper arm and choose the next arm, what will the actual movement of the lower arm will? It is obviously the synthesis of two sports. Therefore, we need to establish a connection (or inheritance) relationship of a bone, then the actual conversion matrix of the child skeleton will be the replacement matrix of the skeleton and the transition matrix of the mother skeleton. This is the essence of skeletal animation technology. It seems that it is not a very complicated look, Hehe. However, this implementation still has a small problem, and Figure 10.6 shows it. Figure 10.9 The effect on the left looks less beautiful, but it is the result of the simple method described above. In order to achieve the right effect, we need to make multiple bones simultaneously affect a vertex. Each vertex in Mesh will be assigned multiple weight (weight), which determines which bones this vertex is influenced. In general, two weight (actually only give a weight of the bones, another weight is generally obviously equal to 1-weight) It is already possible to give good results. Then, we convert the vertices to the matrix of these bones, and then add these results to the right weight. OK, the principle of skeletal animation is over, the next step is to think about how to implement it. 10.8 mirror The effect of the mirror can be considered a mirror in the game, the surface, the water, and smooth floors, etc. To achieve this effect, it is generally necessary to rely on Steencil Buffer technology. Figure 10.10 Mirror effect 10.9 shadow Maybe you will be confused with the lights provided by DXGraphics can't produce shadows. DXGraphics does not do so is that the impact of shadow on the speed of the program is too big. For example, if we want to display a shadow of a person, we must first Figure 10.11 Shadow effect However, in many games (such as Counter strike) we can only see the shadow of the building, and these shadows are fixed, so they have to do much better. We can pre-calculate the range of these shadows and add them to Mesh. When the program is running, just judge whether the character is in the shadow, if it is, it will be dimmed. Chapter 11 I didn't want a good name. To say that it is hot now, it's a online game, and it seems that it will be a general trend. So how can we miss the trend? Because the current network programming is the most common use of Winsock, let's take a look at how to use it. 11.1 Basic Concept Client, Server, Sync, Async ... 11.2 Procedure Process 11.2.1 Server Before calling other Winsock functions, we must first perform WSAStartup () to initialize Winsock and inform Windows to transfer Winsock's DLL to memory. Next, we need to call socket () to create the server-side socket and bind it with a bind () of one of the servers. Then we need to set the network event with WSaasyncselect to process various network events in the message processing function of the window. Finally call listen () to start listening to various network events. When a network event occurs, the window of the window accepts a network message. Depending on the WPARAM parameters of the message, we can understand the type of message to handle the message. For example, if WPARAM is fd_accept, a client request is connected to the server, we can call Accept () Accept and get the client's socket; if wparam is fd_read, then the network event that occurs is a server received a from the client. The information sent, calling RECV () to receive the information. There are two ways to turn off the server. The first is the so-called "elegant approach". It takes a few steps: first call ShutDown (), which will stop sending new information and sending an FD_CLOSE network message to the client (client After this message, you should send the information you want to send, and then call ShutDown () and ClosSocket ()). At this point, we should call RECV () when you receive FD_READ, and then you can closesocket () after receiving the fd_close sent from the client. Finally, WSACLEANUP () can be called to clean the Winsock's DLL. The second way is to "violence the law", the closing speed is fast enough but it is possible to lose the last information. This method is also simply simply, and the closesocket () can be executed directly. 11.2.2 client The initialization process of the client has some similarities between the server side. The first is WSAStartup (), and then execute socket () creates a socket, but there is no need to use a bind () binding interface because no one will connect to the client. There is also a little different from the next registration network event: no fd_accept is fd_connect. Finally, there is enough CONNECT () instruction for connecting the server. After connecting the server, the server will send a FD_WRITE instruction to the customer, indicating that you can send information. At this time, the client can send various information to the server using Send (). Close the steps of the client and the step of shutting down the server are exactly the same. 11.3 Program Example First of all, we must be in the program #include Note this statement must be in #include prior to. Then we have to join WS2_32.lib in the project. Let's take a look at the code of the server (for convenience of explanation). We predefine a class for easy processing: Class Server { PUBLIC: Server (); Void Accept (Socket S); Void Close (Socket S); Void Rece (socket s); Void Send (Socket S); ~ Server (); Socket Server, Client [5]; // Socket for storage server and customer SERVER; Server :: server () // Initialization { For (int i = 0; i <5; i ) Client [i] = null; Wsadata wdata; WSASTARTUP (Makeword (2, 2), & WDATA); // Initializing Winsock Server = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); // Create Server Socket SockAddr_in localaddr; // Store local address Localaddr.sin_Family = AF_INET; // Type is Internet Address localaddr.sin_port = htons (8888); // port 8888 is set to be in the range of 0-65535 localaddr.sin_addr.s_addr = INADDR_ANY; // may receive any connection Bind (STRUCT SOCKADDR *) & localaddr, sizeof (sockaddr)); // Binding address WSaasyncselect (Server, Hwnd, WM_USER 2, // ourselves set a message code FD_ACCEPT | FD_CLOSE | FD_READ | FD_WRITE); // Setting Event Listen (Server, 5); // Start listening, set up to 5 customers } Since we have set a message, you can accept web messages in the message handling function of the server window: WinProc () { Socket S; Ievent; Switch (Message) { Case WM_USER 2: // Handling Network Messages Event = wsagetselectevent (lparam); // get the event code S = (socket) wparam; Switch (Event) { Case fd_accept: // Have a customer attempt to connect Server-> accept (s); Break; Case fd_close: // Any party tries to close the connection Server -> Close (s); Break; Case fd_read: // acceptable information Server -> Receive (s); Break; CASE fd_write: // can send information Server -> Send (s); Break; } Return 0; Case xxxxx: // Handling other messages ...... // Execute CloseSocket (Server) when you want to turn off the server; } Return DefWindowProc (Hwnd, Message, WPARAM, LPARAM); } Void Server :: accept (socket s) // Accept connection { SockAddr_in Address; Int size = sizeof (sockaddr); // Use the customer's socket to the client [] For (int i = 0; i <5; i ) IF (client [i] == null) Client [I] = Accept (Server, (LPSOCKADDR) & address, & size); } Void Server :: Receive (Socket S) // Receive Information { CHAR BUFFER [255]; // Store received information Recv (Client, Buffer, Sizeof (Buffer), 0) } Void Server :: Close (Socket S) // Close the customer socket, use the violent method? { For (int i = 0; i <5; i ) IF (client [i] == s) Client [i] = null; CloseSocket (s); } Next, let's take a look at the client's code: Class Client { PUBLIC: CLIENT (); Void Close (Socket S); Void Rece (socket s); Void Send (Socket S); ~ Client (); Socket client; } Client :: Client () // Initialization { Wsadata wdata; WSASTARTUP (Makeword (2, 2), & WDATA); Client = Socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); WSaasyncselect (Client, Hwnd, WM_USER 2, FD_CONNECT | FD_CLOSE | FD_READ | FD_WRITE); SockAddr_in Target; Target.sin_family = AF_INET; Target.sin_port = htons (8888); // server port Target.sin_addr.s_addr = inet_addr ("127.0.0.1"); // Server IP Connect (Client SockAddr *) & Target, Sizeof (Target)); // Trying to connect } Client :: Send (Socket S) { Char buffer [255]; STRCPY (Buffer, "Haha, I am Connected!"); Send (Client, Buffer, Sizeof (Buffer), 0); } Client :: Close (socket S) { CloseSocket (s); } The client's message handler is like this: WinProc () { Socket S; Ievent; Switch (Message) { Case WM_USER 2: // Handling Network Messages Event = wsagetselectevent (lparam); // get the event code S = (socket) wparam; Switch (Event) { Case FD_CONNECT: / / The result of the connection server has come out / / Here, WsageTlasterror () should be performed, see Section 11.4 for specific methods. Break; Case fd_close: // Any party tries to close the connection CLIENT -> Close (s); Break; Case fd_read: // acceptable information Client -> Receive (s); Break; CASE fd_write: // can send information Client -> Send (s); Break; } Return 0; Case xxxxx: // Handling other messages ...... // Execute CloseSocket (Client) when you want to turn off the client; } Return DefWindowProc (Hwnd, Message, WPARAM, LPARAM); } 11.4 Error handling In Winsock, the chances of various errors are relatively large, because the network itself has certain instability. Of course, the error has occurred, and WsageTlasterror () will return to the previous error, and the meaning of these code can be found in Appendix II. So how do we know if there is any mistake? Of course, it is the return value of the function. Table 11.1 Frequent WINSOCK Function Return Value Return value when the return value fails when the function name is successful WSAStartup () 0 A socket error code WSaasyncselect () 0 Socket_ERROR WSACLANUP () 0 Socket_ERROR Socket () a legitimate socket invalid_socket Bind () 0 Socket_ERROR Listen () 0 Socket_ERROR Connect () 0 Socket_ERROR Accept () a legitimate socket invalid_socket Recv () received how many bytes of byte data SOCKET_ERROR How many bytes of byte data sent () sent SOCKET_ERROR Shutdown () 0 Socket_ERROR CloseSocket () 0 Socket_ERROR Note that Connect () often returns a Socket_ERROR that uses WsagetLastError to know the error code is 10035, which is Connect () takes a while to complete. At this point we should continue to call Connect (), if you return to WsageTlasTERROR, you can know the Socket_ERROR that is 10056, indicating that it is connected. 11.5 Display IP address From the above client program, we can see that the IP address of the server is required when we connect. Although we can get it by running WiniPCFG or IPConfig at the server, it is better if it can be displayed directly on the server screen. The method is like this: Void getip (int stat) { Static char buf [MAXGETHOSTSTRUCT]; // Storage server data IF (stat == 0) // If it is called for the first time { Char name [255]; // Name of the storage server GethostName (Name, 255); // Get the name of the server DWORD add = inet_addr (name); // Try to convert the server name to the Internet address IF (add == inaddr_none) // If you can't convert { WSaasyncGethostByname (hwnd, wm_user 1, Name, buf, maxgethoststruct; // Get IP through the server name } Else { WSaasyncGethOstbyaddr (hwnd, wm_user 1, (const char *) & add, sizeof (in_addr), AF_INET, BUF, MAXGETHOSTSTRUCT); // Get IP through the server address } } ELSE // If you have received WM_USER 1 message { CHAR IP [64]; // Stores the IP address, in the form of xxx.xxx.xxx.xxx LPHOSTENT LPHOSTENT = (LPHOSTENT) BUF; STRCPY (IP, INT_NTOA (* (LPIN_ADDR) LPHOSTENT-> H_ADDR); / / You can put some statements that display IP addresses } } We can call this function after initializing the server, remember that stat = 0. It will register a web message WM_USER 1, if we receive this message in the message loop of the server window, that is, the description has been obtained, you can call Getip (1) to display the IP. 11.6 Transfer data more efficiently The previously described is only how to transfer data, but what data should you send to higher efficiency? This is obvious to set according to the type of game. Chapter 12 Creates our world Because I felt that the game was not very difficult, I felt that I would like to add anything here, thank you! 12.1 Procedure Process In general, the main proxidation of a game using DirectX is like this: Main () { Initialization variables and pointers; Initialization window; Initialize DirectX; Initializing Winsock; For (;;) // main loop { IF (have a message) { IF (for exiting information) { Release DirectX; Release the pointer; drop out; } ELSE IF (for web messages) { deal with; } Else { Call the default message handler; } } ELSE IF (program activation) { Read the keyboard and mouse information by DINPUT; Refresh image; Refresh the other parts of the game; If the device is lost, restore;} Else { Waiting for the message; } } } 12.2 program structure Figure 12.1 The above picture shows the structure of a typical stand-alone game. Just think, look forward to 12.3 Basic Method (1) First move the pen design, then program. A good design allows us to have a half! (2) Step by step, write the frame, slowly expand new features. (3) It is best not to use the framework of others, it is best to write. (4) Make a large change, back up the code before, to ensure that there is always a backup that can run to prevent passion? 12.4 SLG programming points 12.4.1 Computer AI Local AI of RTS is almost 12.5 RPG & ARPG Programming Points 12.5.1 Generation of Maze I will write routines, wait a minute. 12.5.2 Script Technology Actually, it is not very complicated, just take a slightly moving pen design. I don't know why so many people ask 12.6 RTS Programming Points 12.6.1 Looking for A * 加 让? To write routines ... 12.6.2 Computer AI Overall AI: Generally AI scripting technology. But it seems that it is not good. How can I have a big concept? thinking… Local AI: Less blood of the soldiers back (blood or back to the front), concentrated firepower to solve the lowest enemy and high attack power (note It is also possible to concentrate on fire, etc.), and so on. (Plathate WC3 micro-operation?) 12.7 FPS Programming Points 12.7.1 Mobile It can be very simple to implement mobile 3D scenes. The key is what you want to achieve and whether you have clear thinking. Let us still say a very common in the FPS, and the effect is very good: use the mouse to control the sight, with the keyboard control before and after. In order to achieve this effect, of course, the position of the player must be stored (assuming X, Y, Z) and the position of the eye (assuming to TX, TY, TZ). First, add these sentences in the refresh function of the game (the working principle think about it, very simple): / / Assume that mousex is the X coordinate of the mouse, Mousey is the y coordinate of the mouse, and has been used in Section 7.2 using DINPUT IF (Mousey> 236) / / Prevent the Mousey Crossing Border to move the upper and lower movement of the line Mousey = 236; // 236 about equals π * 150/2 IF (mousey <-236) / / Prevent the Mousey Crossing Border to move the upper and lower movement of the line of sight Mousey = -236; Player-> Ty = Player-> Y-TAN ((FLOAT) / 150); // 150 can be changed to other constants Player-> TX = Player-> x sin ((float) (mousex) / 150); Player-> TZ = Player-> Z COS ((FLOAT) (Mousex) / 150); Then set a View matrix like this when the screen is refreshed: D3DXMAMATRIXLOKATLH (& matview, & D3DXVECTOR3 (X, Y, Z), & D3DXVECTOR3 (TX, TY, TZ), & D3DXVECTOR3 (0.0F, 1.0F, 0.0F)); Now, the use of the mouse control line has been implemented. The next step is to join the mobile function, which is also very simple: FLOAT DX = (TX-X) * Time * 7; // Time is a refresh time (second) FLOAT DZ = (tz-z) * Time * 7; // You can modify 7 this number to achieve the speed of X and Z plus or subtract DX and DZ depending on the direction of the moving direction. The system is very easy? 12.7.2 Collision Detection Don't think about 3D collision testing is too difficult (although it is really difficult), because the thoughtful Microsoft programmer has already thought of our difficulties (it's hard). A function that provides a function can be greatly located in DXgraphics, which is D3DXINTERSECT (), which is to determine whether a ray in the space intersects with the specified MESH, if intersects, will return the distance. Therefore, as long as it comes from the person, it is less than a certain value that will be moved from the person, and then determine if the distance from any MESH is less than a value, it can complete almost completely accurate collision detection. The only problem is that the characters may intersect in other directions, but this kind of probability is not large, and some can solve the number of rays ---- The current CPU is so fast, can we lazy? The prototype of this function is: D3DXINTERSECT LPD3DXBASEMESH PMESH, / / Mesh to test CONST D3DXVECTOR3 * PRAYPOS, / / rays start Const d3dxvector3 * praydir, // ray direction BOOL * phit, // Do you intersect? DWORD * PFACEINDEX, // intersects at the first triangle in Mesh FLOAT * PU, / / Where to do in the triangle Float * pv, // Where is the triangle? Float * pdist, // Distance LPD3DXBUFFER * PPALLHITS, / / Save all intersection information DWORD * PCOUNTOFHITS / / ); Only one point to explain: What should be in the direction of the ray? Very simple, assuming that the starting point is (x1, y1, z1), the end point is (x2, y2, z2), the direction is (X2-X1, Y2-Y1, Z2-Z1). However, if there is more than the polygon in the scene, the efficiency of collision detection can be relatively low, the solution is to use Bounding Box & Bounding Sphere, that is, the object is constructed by some of the rectangles and balls, and then replaces collision Detection. 12.8 Physics in the game Haha, I like this section? Here will introduce some physical basis knowledge to you. Appendix Appendix a list of common messages WM_ACTIVATE INDICATES A CHANGE IN THE Activation State WM_ACTIVATEAPP NOTIFIES Applications WHEN A New Task is Activated WM_CANCELMODE NOTIFIFIES A Window to Cancel Internal Modes WM_CHANGECBCHAIN Notifies Clipboard Viewer of Removal from Chain WM_CHAR PASSES Keyboard Events to Focus Window WM_CHARTOITEM Provides List-Box Keystrokes to Owner Window WM_ChildActivate Notifies a Child Window of Activation WM_CLEAR CLEARS AN Edit Control or Combo Box WM_Close Signals A Window or Application To Terminate WM_COMMAND Specifies a Command Message WM_COMMNOTIFY NOTIFIES A WINDOW ABOUT The Status of Its Queueswm_compacting Indicates a Low Memory Condition WM_CompareItem DETERMINES POSITION OF Combo-Box or List-Box Item WM_COPY COPIES A Selection to The Clipboard WM_CREATE INDICATES THATED A Window Is Being Created WM_CTLCOLOR INDICATES THAT A Control Is About To Be Drawn WM_CUT DELETES A Selection and Copies It To The Clipboard WM_DEADCHAR INDICATES WHEN A DEAD KEY IS PRESSED WM_DELETEITEM INDICES OWNER-DRAWN ITEM OR Control Is Altered WM_DESTROY INDICATES WINDOW IS ABOUT to Be DESTROYED WM_DEVMODECHANGE INDICATES WHEN Device-Mode Settings Are Changed WM_DrawClipboard INDICES WHEN CLIPBOARD CONTENTS Are Changed WM_DRAWITEM INDICES WHEN OWNER-DRAWN Control Or Menu Changes WM_DROPFILES INDICES WHEN A File Is Dropped WM_ENABLE INDICATES WHEN ENABLE State of window is Changing WM_ENDSESSION INDICATES WHETHER THE Windows Session IS Ending WM_ENTERIDLE INDICATES A Modal Dialog Box Or Menu IS iDLE WM_ERASEBKGND INDICES WHEN Background of Window Needs Erasing WM_FONTCHANGE INDICES A CHANGE IN THE FONT-RESOURCE POL WM_GETDLGCODE Allows Processing Of Control Input WM_GETFONT RETRIEVES The Font That A Control IS Us WM_GETMINMAXINFO RETRIEVES Minimum and Maximum SIZING INFORMATION WM_Gettext Copies The Text That Corresponds to a Window WM_GETTEXTLENGTH DETERMINES Length of Text Associated with a window WM_HSCROLL INDICATES A CLICK IN A HORIZONTAL SCROLL BAR WM_ICONERASEBKGND NOTIFIFIES minimized window to Fill icon background WM_INITDIALOG Initializes A Dialog Box WM_INITMENU INDICES WHEN A Menu Is About To Become Active WM_INITMENUPOPUP INDICATES WHEN A POP-UP MENU IS Being Created WM_KEYDOWN INDICATES WHEN A Nonsystem Key IS PRESSED WM_KEYUP INDICATES WHEN A NONSYSTEM KEY IS Released WM_KILLFOCUS INDICES WINDOW IS ABOUT to LOSE INPUT FOCUS WM_LBUTTONDBLCLK INDICES DOUBLE-CLICK OF LEFT MOUSE button WM_LBUTTONDOWN INDICES WHEN LEFT MOUSE Button IS PRESSED WM_LBUTTONUP INDICES WHEN LEFT MOUSE button is released WM_MBUTTONDBLCLK INDICATES DOUBLE-CLICK OF MIDDLE MOUSE button WM_MBUTTONDOWN INDICES WHEN MIDDLE MOUSE Button IS PRESSED WM_MBUTTONUP INDICES WHEN MIDDLE MOUSE button is released WM_MDIACTIVATE ACTIVATIVATES A New MDI Child Window WM_MDICASCADE ARRANGES MDI CHILD Windows in a Cascade Format WM_mdicreate Prompts an MDI Client To Create a Child Window WM_MDIDESTROY Closes an MDI Child Window WM_MDiGetActive Retrieves Data About The Active MDI Child Window WM_MDIICONARRANGE ARRANGES Minimized MDI Child Windows WM_MDIMAXIMIZE MAXIMIMIZES AN MDI CHILD WINDOW WM_MDINEXT ACTIVATES The Next MDI CHILD WINDOW WM_MDIRESTORE PROMPTS AN MDI Client To Restore a Child WINDOW WM_MDisetMenu Replace The Menu of A MDI Frame Window WM_MDITILE ARRANGES MDI CHILD Windows in a Tiled Format WM_MeasureItem Requests Dimensions of Owner-Drawn Control WM_MENUCHAR INDICATES WHEN UNKNOWN MNEMONIC IS PRESSED WM_MENUSELECT INDICES WHEN A Menu Item Is SELECTED WM_MOUSEACTIVATE INDICATES A MOUSE CLICK IN ANACTIVE WINDOW WM_MouseMove Indicates Mouse-Cursor Movement WM_MOVE INDICATES The Position of A Window Has Changed WM_NCActivate Changes The Active State of A Nonclient Area Wm_nccalcsize calculates the size of a window's client isa WM_NCCREATE INDICATES A NONCLIENT Area Is Being Created WM_NCDESTROY INDICES WHEN NONCLIENT Area Is Being Destroyed WM_NCHITTEST INDICATES MOUse-Cursor Movement WM_NCLBUTTONDBLCLK INDICATES NON-Client Left Button Double-ClickWM_NCLBUTTONDOWN INDICES Left Button Pressed in Nonclient Area WM_NCLBUTTONUP INDICATES LEFT button Released in Nonclient Area Wm_ncmbuttondblclk INDICES MIDDLE button Nonclient Double-Click Wm_ncmbuttondown INDICES MIDDLE Button Pressed in Nonclient Area WM_NCMBUTTONUP INDICATES MIDDLE button Released in Nonclient Area WM_NCMOUSEMOVE INDICATES MOUse-Cursor Movement in Nonclient Area WM_NCPAINT INDICATES A WINDOW's Frame Needs Painting Wm_ncrbuttondblclk Indicates Right Button Nonclient Double-Click WM_NCRBUTTONDOWN INDICAES Right Button Pressed in Nonclient Area WM_NCRBUTTONUP INDICAES Right Button Released in Nonclient Area WM_NEXTDLGCTL Sets The Focus to a Different Dialog Box Control WM_Paint Indicates A Window Frame Needs Painting WM_PaintClipboard Paints The Specified Portion of The Window WM_PALETTECHANGED INDICATES FOCUS-WINDOW HAS REALIZED ITS PALETTE WM_PALETTEISCHANGINGINGINGINGINGINGINS Windows About Change to Palette WM_ParentNotify Notifies Parent of Child-Window Activity WM_Paste Inserts Clipboard Data Into An Edit Control WM_Power Idicates The System Is Entering Suspended Mode WM_QueryDragicon Requests a Cursor Handle for a minimized window WM_QUERYENDSESSION Requests That The Windows Session Be Ended WM_QUERYNEWPALETTE Allows A Window To Realize ITS Logical Palette WM_QUERYOPEN Requests That a minimized window be rest WM_QUEUESYNC DELIMITS CBT MESSAGES WM_Quit Requests That An Application Be Terminated WM_RBUTTONDBLCLK INDICATES A DOUBLE-CLICK OF Right Mouse Button WM_RBUTTONDOWN INDICES WHEN THE RIGHT MOUSE Button IS PRESSED WM_RBUTTONUP INDICATES WHEN THE RIGHT MOUSE Button IS RELEASED WM_RenderalLFormats Notifies Owner To Render All Clipboard Formatswm_renderformat Notifies Owner To Render Particular Clipboard Data WM_SETCURSOR DISPLAYS The Appropriate Mouse Cursor Shape WM_SETFOCUS INDICATES WHEN A WINDOW HAS Gained Input Focus WM_SETFONT STS The Font for A Control WM_SETREDRAW Allows or Prevents Redrawing in A Window WM_SETTEXT SETS The Text of A Window WM_SHOWINDOW INDICATES A WINDOW IS ABOWN WM_SIZE INDICATES A CHANGE IN WINDOW SIZE WM_SIZECLIPBOARD INDICATES A CHANGE IN CLIPBOARD SIZE WM_SPOOLERSTATUS INDICES WHEN a Print Job Is Added or Removed WM_SYSCHAR INDICATES WHEN A System-Menu Key Is Pressed WM_SYSCOLORCHANGE INDICES WHEN A System Color Setting IS Changed WM_SYSCOMMAND INDICATES WHEN A System-Command Is Requested WM_SYSDEADCHAR INDICATES WHEN A System Dead Key Is Pressed WM_SYSKEYDOWN INDICES THAT PLUS ANOTHER Key Was Pressed WM_SYSKEYUP INDICES THAT PLUS ANOTHER Key Was Released WM_SYSTEMERROR INDICATES THAT A System Error Has Occurre WM_TIMECHANGE INDICES That The System Time Has Been Set WM_TIMER INDICAS TIMEOUT Interval for A Timer Has Elapsed WM_UNDO undoes the last operation in an Edit Control WM_USER INDICATES A RANGE OF MESSAGE VALUES WM_VKEYTOITEM Provides List-Box Keystrokes To Owner Window WM_VSCROLL INDICATES A CLICK IN A VERTICAL SCROLL BAR WM_VSCROLLLPBOARD Prompts The Owner To Scroll Clipboard Contents WM_WindowPoschanged Notifies A Window of a size or position change WM_WindowPoschanging Notifies a Window of a new size or position Appendix bin virtual key list Virtual key in the Windows message VK_LBUTTON Mouse Left button 0x01 VK_RBUTTON mouse button 0x02 VK_CANCEL CTRL BREAK 0x03 VK_MBUTTON mouse button 0x04 VK_BACK Backspace button 0x08 VK_TAB TAB button 0x09 VK_RETURN Enter Key 0x0DVK_SHIFT button 0x10 VK_CONTROL CTRL button 0x11 VK_MENU ALT button 0x12 VK_PAUSE PAUSE button 0x13 VK_Capital Caps Lock button 0x14 VK_ESCAPE ESC button 0x1b VK_SPACE Space Bar 0x20 VK_PRIOR PAGE UP 2X21 VK_NEXT PAGE DOWN button 0x22 VK_END END button 0x23 VK_HOME HOME button 0x24 VK_LEFT left arrow key 0x25 VK_UP on arrow keys 0x26 VK_RIGHT right arrow button 0x27 VK_DOWN down arrow key 0x28 VK_Snapshot Print Screen button 0x2c VK_INSERT INSERT button 0x2D VK_DELETE DELETE button 0x2e '0' - '9' Number 0 - 9 0x30 - 0x39 'A' - 'Z' Letter A - Z 0x41 - 0x5A VK_LWIN left WINKEY (104 keyboard is only) 0x5b VK_RWIN Right WinKey (104 keyboard is only) 0x5c VK_APPS AppsKey (104 keyboard is available) 0x5D VK_NUMPAD0 keypad 0 key 0x60 VK_NUMPAD1 keypad 1 button 0x61 VK_NUMPAD2 keypad 2 key 0x62 VK_NUMPAD3 keypad 3 key 0x63 VK_NUMPAD4 keypad 4 key 0x64 VK_NUMPAD5 keypad 5 key 0x65 VK_NUMPAD6 keypad 6 key 0x66 VK_NUMPAD7 keypad 7 button 0x67 VK_NUMPAD8 keypad 8 key 0x68 VK_NUMPAD9 keypad 9 key 0x69 VK_F1 - VK_F24 function key F1 - F24 0x70 - 0x87 VK_NUMLOCK NUM LOCK button 0x90 VK_SCROLL SCROLL LOCK button 0x91 Virtual key in DirectInput DIK_0 - DIK_9 Number 0 - 9 DIK_A - DIK_Z letters a - z DIK_F1 - DIK_F12 function keys F1 - F12 DIK_BACK Backspace button DIK_TAB TAB button DIK_RETURN Enter key DIK_LSHIFT Left Shift Key DIK_RSHIFT right shift keys Dik_lcontrol Left Ctrl Key DIK_RCONTROL Right Ctrl Key Dik_lmenu left ALT key DIK_RMENU Right Alt Key DIK_PAUSE PAUSE button DIK_CAPITAL CAPS LOCK button DIK_ESCAPE ESC DIK_SPACE Spacebar DIK_PRIOR PAGE UP DIK_NEXT PAGE DOWN DIK_END END button DIK_HOME HOME button DIK_LEFT left arrow key Dik_up on arrow keys DIK_RIGHT right arrow key Dik_down down arrow keys DIK_SYSRQ SYSRQ button DIK_INSERT INSERT button DIK_DELETE DELETE button Dik_LWIN left WINKEY (104 keyboard is only) Dik_rwin right WinKey (104 keyboard is only) Dik_APPS Appskey (104 keyboard is only) DIK_NUMPAD0 - DIK_NUMPAD0 keypad 0 - 9 key DIK_NUMLOCK NUM LOCK button DIK_SCROLL SCROLL LOCK button Appendix 3 DirectX Function Return Value List DirectDraw section DD_OK The request completed successfully.dderr_alreadyInitialized The Object Has Already Been Initialized. DDERR_BLTFASTCANTCLIP ............................. DDERR_CANNOTATTACHSURFACE A Surface Cannot Be attached to another Requested Surface. DDERR_CANNOTDETACHSURFACE A Surface Cannot Be detached from another requested surface. DDERR_CANTCREATEDC Windows can not create any more device contexts (DCs), or a DC was requested for a palette-indexed surface when the surface had no palette and the display mode was not palette-indexed (in this case DirectDraw can not select a proper palette into the DC). DDERR_CANTDUPLICATE PRIMARY AND 3-D SURFCES, OR SURFCES THAT ARE IMPLICITLY CREATED, Cannot Be duplicated. DDERR_CANTLOCKSURFACE Access to this Surface Is Refused Because An Attempt Was Made to Lock The Primary Surface WITHOUT DCI Support. DDERR_CANTPAGELOCK An Attempt to Page Lock A Surface Failed. Page Lock Will Not Work ON A Display-Memory Surface OR An Emulated Primary Surface. DDERR_CANTPAGEUNLOCK An Attempt to Page Unlock A Surface Failed. Page Unlock Will Not Work ON A Display-Memory Surface OR An Emulated Primary Surface. DDERR_CLIPPERISUSINGHWND An Attempt Was Made To Set a Clip List for a DirectDrawClipper Object That Is Already Monitoring A Window Handle. DDERR_COLORKEYNOTSET No Source Color Key Is Specified for this Operation. DDERR_CURRENTLYNOTAVAIL No support is currently available. DDERR_DCALREADYCREATED A Device Context (DC) HAS Already Been Returned for this Surface. Only One DC CAN Be Retrieved for Each Surface. DDERR_DEVICEDOESNTOWNSURFACE Surfaces create by one directdraw device cannot be used Directly by Another DirectDraw device.dderR_directdrawalreadycreated A DirectDraw Object Representing this Driver Has Already Been Created for this process. DDERR_EXCEPTION An Exception Was Encountered While Performing The Requested Operation. Dderr_ExClusiveModeAlReadyset An Attempt Was Made to set the cooperative level1 at the coope set to excel. DDERR_EXPIRED The Data Has Expired and is ThereFore No Longer Valid. DDERR_GENERIC There is an undefined error condition. DDERR_HEIGHTALIGN The Height of the Provided Rectangle is Not a Multiple of The Required Alignment. Dderr_hwndalreadyset The DirectDraw Coooperative Level Window Has Already Been Set. It Cannot Be Reset While The Process Has Surfaces OR PALETES CREATED. Dderr_hwndsubclassed DirectDraw Is Prevented from Restoring State Because The DirectDraw Cooperative Level Window Handle Has Been Subclassed. DDERR_IMPLICITLYCREATED The Surface Cannot Be Restored Because It is an in Implicitly Created Surface. DDERR_INCOMPATIBLEPRIMARY The Primary Surface Creation Request Does Not Match with the existing primary surface. DDERR_INVALIDCAPS One or more of the capability bits passed to the callback function is incorrect. DDERR_INVALIDCLIPLIPLIST DirectDraw Does Not Support The Provided Clip List. DDERR_INVALIDDIRECTDRAWGUID The Globally Unique Identifier (GUID) Passed to The DirectDrawcreate Function is Not a Valid DirectDraw Driver Identifier. DDERR_INVALIDMODE DirectDraw Does Not Support The Requested Mode. DDERR_INVALIDOBJECT DirectDraw Received A Pointer That Was An Invalid DirectDraw Object. DDERR_INVALIDPARAMS One or more of the parameters passed to the method is incorrect.dderr_invalidpixelformat The Pixel Format Was Invalid As Specified. DDERR_INVALIDPSITION The position of the overlay on the destination is no longer legal. DDERR_INVALIDRECT The provided reccTangle Was INVALID. DDERR_INVALIDSTREAM The Specified Stream Contains INVALID DATA. DDERR_INVALIDSURFACTYPE The Requested Operation Could Not Be Performed Because The Surface Was of The Wrong Type. DDERR_LOCKEDSURFCESURES One or more Surfaces Are Locked, Causeing The Failure of The Requested Operation. DDERR_MOREDATA There Is More Data Available Than The Specified Buffer Size Can Hold. DDERR_NO3D NO 3-D Hardware or Emulation IS present. DDERR_NOALPHAHW No alpha acceleration hardware is present or available, causing the failure of the review. DDERR_NOBLTHW No blitter hardware is present. DDERR_NOCLIPLIST NO Clip List is available. DDERR_NOCLIPPERATTACHED No DirectDrawClipper Object Is Attached to The Surface Object. DDERR_NOCOLORCONVHW The Operation Cannot Be Carried Out Because No Color-Conversion Hardware Is Present OR Available. DDERR_NOCOLORKEY The Surface Does Not Currently Have A Color Key. DDERR_NOCOLORKEYHW The Operation Cannot Be Carried Out Because The Des No Hardware Support for the Destination Color Key. DDERR_NOCOOPERATIVELEVELSET A create function is called without the iDirectdraw7 :: setCooperativeElevelMethod being called. DDERR_NODC NO DC HAS EVER Been Created for this Surface. DDERR_NODDROPSHW No DirectDraw Raster Operation (ROP) Hardware is Available. DDERR_NODIRECTDRAWHW Hardware-Only DirectDraw Object Creation is not possible; The Driver Does Not Support Any Hardware. DDERR_NODIRECTDRAWSUPPORT DirectDraw Support Is Not Possible with The Current Display Driver.dderr_noemulation Software emulation is not available. DDERR_NOEXCLUSIVEMODE The Operation Requires The Application To Have Exclusive Mode, But The Application Does Not Have Exclusive Mode. DDERR_NOFLIPHW Flipping Visible Surfaces IS Not Supported. DDERR_NOFOCUSWINDOW An Attempt Was Made to Create Or Set a Device Window WITHOUT FIRST SETTING THE FOCUS WINDOW. DDERR_NOGDI NO GDI IS Present. DDERR_NOHWND Clipper Notification Requires A Window Handle, or No Window Handle Has Been Previously Set As The Cooperative Level WINDOW HANDLE. DDERR_NOMIPMAPHW ..................... DDERR_NOMIRRORHW The Operation Cannot Be Carried Out Because No mirroring hardware is present or available. DDERR_NONONONLOCALVIDMEM An Attempt Was Made to Allocate Non-local Video Memory from a Device That Does Not Support Non-Local Video Memory. DDERR_NOOPTIMizehw The Device Does Not Support Optimized Surfaces. DDERR_NOOVERLAYDEST The iDirectdrawsurface :: getoverlayPosition Method Is Called on an offlay That The IdirectDrawSurface :: Updateoverlay Method Has Not Been Called on To Establish A Destination. DDERR_NOOVERLAYHW The Operation Cannot Be Carried Out Because No Overlay Hardware is present or available. DDERR_NOPALETTEATTACHED No Palette Object Is Attached to this Surface. DDERR_NOPALETTEHW There is no hardware support for 16- or 256-color pastetes. Dderr_noSterophw The Operation Cannot Be Carried Out Because No Appropriate Raster Operation Hardware is present or available. DDERR_NOROTIONHW The Operation Cannot Be Carried Out Because No Rotation Hardware Is Present or Available.dderr_nostRetchhw The Operation Cannot Be Carried Out Because The IS No Hardware Support for Stretching. DDERR_NOT4BITCOLOR . DDERR_NOT4BITCOLORINDEX The DirectDrawSurface Object Is Not Using A 4-Bit Color Index Palette and The Requested Operation Requires A 4-Bit Color Index Palette. DDERR_NOT8BITCOLOR The DirectDrawSurface Object Is Not Using An 8-Bit Color Palette and The Requested Operation Requires An 8-Bit Color Palette. DDERR_NOTAOVERLAYSURFACE An Overlay Component Is Called for a Non-overlay Surface. DDERR_NOTEXTUREHW The Operation Cannot Be Carried Out Because No Texture-Mapping Hardware is present or available. DDERR_NOTFLIPPABLE An Attempt Has Been Made to Flip A Surface That Cannot Be Flipped. DDERR_NOTFOUND The Requested Item Was Not Found. DDERR_NOTINIALIZED An Attempt Was Made to Call An Interface Method of A DirectDraw Object Created by COCREATEINSTANCE BEFORE The Object Was Initialized. DDERR_NOTLOADED The Surface Is An Optimized Surface, But It Has Not Yet Been Allocated Any Memory. DDERR_NOTLOCKED An Attempt is Made to Unlock A Surface That Was Not Locked. DDERR_NOTPAGELOCKED An Attempt is Made to Page Unlock A Surface with no outstanding page locks. DDERR_NOTPALETTIZED The Surface Being Used Is Not a Palette-Based Surface. DDERR_NOVSYNCHW ............ DDERR_NOZBUFFERHW THE OPERATION TO CREATE A Z-Buffer in Display Memory or To Perform A Blit Using A Z-Buffer Cannot Be Carried Out Because The No Hardware Support for Z-Buffers.dderr_NozoverLayHW . DDERR_OUTOFCAPS The Hardware Needed for the Request Operation Has Already Been Allocated. Dderr_OutofMemory DirectDraw Does Not Have Enough Memory To Perform The Operation. Dderr_OutofVideoMory DirectDraw Does Not Have Enough Display Memory To Perform The Operation. DDERR_OVERLAPPINGRECTS . DDERR_OVERLAYCANTCLIP The Hardware Does Not Support Clipped Overlays. DDERR_OVERLAYCOLORKEYOONEOCTIVE An Attempt Was Made to Have More Than One Color Key Active ON overlay. DDERR_OVERLAYNOTVISIBLE The iDirectdrawsurface4 :: GetoverlayPosition Method Is Called on a hidden overlay. DDERR_PALETTEBUSY Access to this Palette Is Refused Because The Palette Is Locked by Another Thread. DDERR_PRIMARYSURFACEALREADYEXISTS This Process Has Already CREATED A Primary Surface. DDERR_REGONTOOSMALL The region passed to the iDirectdrawclipper :: getCliplist method is to small. Dderr_surfaceAlReadyattached An Attempt Was Made to Attach A Surface To Another Surface To Which It Is Already Attached. DDERR_SURFACEALREADYDEPENDENT An Attempt Was Made to make A Surface A Dependency of Another Surface To Which it is already Dependent. DDERR_SURFACEBUSY Access to the surface is reasoned Because The Surface Is Locked by Another Thread. DDERR_SURFACEISOBSCURED Access to the Surface Is Refused Because The Surface Is Obscured.dderr_surfacelost Access to the Surface Is Refused Because The Surface Memory IS GONE. CALL The IDirectdrawsurface :: Restore Method on this Surface To restore the memory associated with it. Dderr_surfacenotattached The Requested Surface Is Not attached. DDERR_TOOBIGHEIGHT The Height Requested by DirectDraw Is Too Large. DDERR_TOOBIGSIZE THE SIZE Requested by DirectDraw Is Too Large. However, The Individual Height and Width Are Valid Sizes. DDERR_TOOBIGWIDTH The Width Requested by DirectDraw Is Too Large. DDERR_UNSUPPORTED The Operation is not supported. DDERR_UNSUPPORTEDFORMAT The Pixel Format Requested is Not Supported by DirectDraw. DDERR_UNSUPPORTEDMASK The Bitmask in the Pixel Format Requested is not supported by DirectDraw. DDERR_UNSUPPORTEDMODE The display is currently in an unsupported mode. DDERR_VERTICALBLANKINPROGRESS A Vertical Blank is in progravel. DDERR_VIDEONOTACTIVE The video port is not active. DDERR_WASSTILLDRAWING The Previous Blit Operation That Is Transferring Information To or from this Surface Is Incomplete. DDERR_WRONGMODE This Surface Cannot Be Restored Because IT Was Created In A Different Mode. DDERR_XALIGN The Provided Rectangle Was Not Horizontally Aligned on A Required Boundary. Direct3D section D3D_OK No error accurred. D3DERR_CONFLICTINGRENDERSTATE . D3DERR_CONFLICTINGTEXTUREFILTER The Current Texture Filters Cannot Be Used Together. D3DERR_CONFLICTINGTEXTUREPALETTE The current textures can not be used simultaneously. This generally occurs when a multitexture device requires that all palletized textures simultaneously enabled also share the same palette.D3DERR_DEVICELOST . D3DERR_DEVICENOTRRESet The Device Cannot Be Reset. D3DERR_DRIVERINTERNALROR INTERNAL DRIVER ERROR. D3DERR_INVALIDCALL . D3DERR_INVALIDDEVICE The Requested Device Type is not valid. D3DERR_MOREDATA There Is More Data Available Than The Specified Buffer Size Can Hold. D3DERR_NOTAVAILABLE THIS Device Does Not Support The Queeried Technique. D3DERR_NOTFOUND The Requested Item Was Not Found. D3DERR_OUTOFVIDEMEMORY Direct3d Does Not Have Enough Display Memory To Perform The Operation. D3DERR_TOOMAYOPERATIONS The Application IS Requesting More Texture-Filtering Operations Than The Device Supports. D3DERR_UNSUPPORTEDALPHAARG The Device Does Not Support a Specified Texture-Blending Argument for the alpha channeling. D3DERR_UNSUPPORTEDALPHAOPERATION The Device Does Not Support a Specified Texture-Blending Operation for the Alpha Channel. D3DERR_UNSUPPORTEDCOLORARG The Device Does Not Support a Specified Texture-Blending Argument for Color Values. D3DERR_UNSUPPORTEDCOLOROPERATION The Device Does Not Support a Specified Texture-Blending Operation for Color Values. D3DERR_UNSUPPORTEDFAACTORVALUE The Device Does Not Support The Specified Texture Factor Value. D3DERR_UNSUPPORTEDTEXTAXTUREFILTER The Device Does Not Support The Specified Texture Filter. D3DERR_WRONTEXTUREFORMAT The Pixel Format of The Texture Surface Is Not Valid.e_fail An Undetermined Error Occurred Inside The Direct3D Subsystem. E_INVALIDARG An Invalid Parameter Was Passed to the Returning Function E_INVALIDCALL . E_OUTOFMEMORY Direct3d Could Not Allocate Sufficient Memory To Complete The Call. S_ok No error accurred. Appendix 4 WINSOCK Function Return Value List WSAEINTR (10004) Interrupted Function Call - A Blocking Operation Was Cancelle. WSAEAccess (10013) Permission Denied - An Attempt to Access A Socket Was Forbidden By ITS Access Permissions. WSAEFAULT (10014) Bad Address - An Invalid Pointer Address Was Specified In A Function Call. WSAEINVAL (10022) Invalid Argument - An Invalid Argument Was Passed to a function. WSAEMFILE (10024) Too Many Open Files - There Are Too Many Open Sockets. WSAEWOULDBLOCK (10035) Resource Temporalily Unavailable - The Specified Socket Operation Cannot Be Completed Immedieration, But The Operation Should Be Retried Later. WSAEINPROGRESS (10036) Operation Now in Progress - A Blocking Operation IS Currently Executing. WSAEAALREADY (10037) Operation Already in Progress - An Operation Was Attempted ON A Non-Binding Socket That Already HAD An Operation In Progress. WSAENOTSOCK (10038) Socket Operation On Non-Socket - An Operation Was Attempted On Sometting That Is Not A Socket. WSAEDESTADDRREQ (10039) Destination Address Required - a Required Address Was Omitted from A Socket Operation. WSaemsgsize (10040) Message Too Long - a Message Was Sent on a DataGram Socket That Exceeds The Internal Message Buffer Other intel. WSAEPROTYPE (10041) Protocol WRONG TYPE for Socket - a protocol WAS Specified That IS Not Supported by The Target Socket.wsaenoprotoPt (10042) Bad Protocol Option - An Unknown, Invalid, or Unsupported Protocol Option or Leel Was Specified. WSAEPROTONOSUPPORT (10043) Protocol Not Supported - The Specified Protocol Is Not Supported or Is Not Implement. WSAESOCKTNOSUPPORT (10044) Socket Type Not Supported - The Specified Socket Type IS Not Supported in The Address Family. WSAEOPNOTSUPP (10045) Operation not supported - The specified operation is not supported by the reference object. WSAEPFNOSupport (10046) Protocol Family Not Supported - The Specified Protocol Family Is Not Supported or Is Not Implement. WSAEafnosupport (10047) Address Family NOT Supported by Protocol Family - An Address Incompatible with the Requested Network Protocol WAS Used. WSAEADDRINUSE (10048) Address Already in Use - An Attempt To Use The Same IP Address and Port with Two Different Sockets Simultaneously Was Made. WSAEADDRNOTAVAIL (10049) Given the Context of the function. WSAENETDOWN (10050) Network is down - a socket Operation Encountered A Network That Is Down. WSAENETUNREACH (10051) NetWork is unreachable - a socket operation encountered an unreachable network. WSAENETRESet (10052) Network Dropped Connection On Reset - a connection "keep-alive" Activity Detecting a failure. Wsaeconnaborted (10053) Software Caused Connection Abort - a Connection Was Aborted by Software on The Host Computer. WSAECONNRESET (10054) Connection Reset by Peer - a connection... WSAenobufs (10055) NO BUFFER SPACE AVAILABLE - A Socket Operation Could Not Be Performed Because The System Ran Out of Buffer Space or The Queue Was Full.wsaeisconn (10056) Socket IS Already Connected - A Connect Request Was Made On A Socket That IS Already Connected. WSAENOTCONN (10057) Socket is not connection - an attempt to send or receive data failed Because The socket is not connection. WSAESHUTDOWN (10058) CANNOT SEND AFTER SOCKET SHUTDOWN - An Attempt To Send or Receive Data Failed Because The socket Has Already Been Shut Down. WSaetimedout (10060) Connection Timed Out - The Remote Host Failed To Respond Withnin The Timeout Period. WSAEConnrefused (10061) Connection Refused - The Target Machine Actively Refused The Attempt To Connect To IT. WSAEHOSTDOWN (10064) Host Is Down - The Destination Host Is Down. WSAEHOSTUNREACH (10065) No route to host - The destination host is unreachable. WSAEPROCLIM (10067) . WSASYSNOTREADY (10091) Network subsystem is unavailable - The underlying system to provide network services is UNAVAILABLE. WSAVERNOTSUPPORTED (10092) Winsock.dll Version Out of Range - The Winsock Implementation Does Not Support The Requested Winsock Version. WSANotinitialized (10093) Successful WSAStartup Not Yet Performed - The Calling Application Has NOT SUCCESSFULLY CALLED WSASTARTAP To INITIATE A WINSOCK session. WSaediscon (10094) Graceful Shutdown in Progress - The Remote Party Has Ired A Graceful Shutdown Sequence. WSATYPE_NOT_FOND (10109) Class Type Not Found - The Specified Class Was Not Found. WSAHOST_NOT_FOND (11001) Host Not Found - No NetWork Host Matching The Hostname Or Address Was Found.wsatry_again (11002) Non-Authoritative Host Not Found - A Temporary Error While Resolving a hostname hostname occured, and shop be retried lat WSANO_RECOVERY (11003) This Is A Non-Recoverable Error - Some Sort of Non-Recoverable Error Occured During A Database Lookup. WSANO_DATA (11004) Valid Name, No Data Record of Requested Type - The Requested Name Is Valid and Was Found, But Does Not Have The Associated Data Requested. Appendix Five Game Programming Common URL Sina.com Making Forum Http://newbbs2.sina.com.cn/index.shtml?games :Gamedesign 17173 Game Alliance Forum http://bbs.17173.com.cn/default.asp 9CBS expert clinic http://www.9cbs.net/expert/forum.asp?roomid=12&typenum=2 Chinese game developer http://mays.soage.com/ China Game Development Technology Resource Network http://www.gameres.com/ Cloud style studio http://linux.163.com/cloudwu/2000/index.html Imagic Studio http://www.imagic3d.com/cindex.html Game1st http://www.game1st.com/cindex.htm How hard http://www.npc6.com/index.htm Gold point time http://www.gpgame.com/ Gamedev (English) http://www.gamedev.net FLIPCODE (English) http://www.flipcode.com Appendix Six Chinese English Name Control Back Buffer Background Cache Buffer cache Class class Color Key Key Color, Transparent Color Cooperative Level Control Level, Collaboration OFF-screen Surface off-screen page Material material, material PRIMARY SURFACE Homepage Sprite Elf Surface page, surface Template Template Texture texture, texture Union union Vertex vertex Appendix Seven Frequently Asked Questions and Solutions 1. "Warning" appears when compiling Say it is "warning", it will not be a big problem, you can ignore them. However, sometimes the operation of the program is caused by these WARNING, especially the type conversion warning. 2. "Cannot Execute Program" Exit VC.NET and restart it, you can also try Build --- Rebuild Solution. If it is not yet, check if the program is running. No, go to the engineering attribute to see if the EXE output path is set. 3. "UnreSolved External Symbol" If "main" or "WinMain" is like, you must choose the wrong engineering type: Is it Win32 Application or Win32 Console Application? If the name or function name appears, then you don't have access to all the LIBs that should be added in the project, or the extern variable you define is not in the extern form. See Section 1.1 and 1.8. 4. Error running 95% of the runtime error is caused by the pointer. So now I will immediately enter the debug mode, then press F5 to execute the code, see which line code is wrong at the end. Pay special attention to whether the pointer of this row is not initialized and whether it is off. 5. What else is there, can tell me? <