Windows编程概述
DirectX,流行的游戏编程库。它上手易,精通难。
Windows is a multi-tasking, multi-threaded operating system. What this means is that Windows can run many programs at the same time, and each of those programs can have several threads running as well. As you might imagine, this operating system architecture works well with multi-core processors.
获取Windows
旧版本Windows的软件(游戏除外,因PC规格日新月异)无需过多修改便可在新版本Windows运行。
Windows编程可简可繁。
Windows是优秀多任务操作系统,因为它是消息驱动的。
理解Windows消息机制
外部事件,如鼠标单击,会导致小的电信号从鼠标转移到USB口再进入系统总线。Windows操作系统从系统总线拾取这一信号并且生成一个消息传递给正在运行的应用程序(比如我们的游戏),程序对这消息做出反应。
计算机的潜意识(处理所有时间处理逻辑的操作系统)将这一事件“呈现”给程序,让其知晓。
DirectX 9旧版依然很好只在现在流行Windows7。
多任务
Windows使用抢占式任务preemptive multi-tasking。
This means that your PC can run many programs at the same time. Windows accomplishes this by running each program for a very short amount of time, counted in microseconds, or millionths of a second.
This jumping from one program to another very quickly is called time slicing时间分片, and Windows handles time slicing by creating a virtual address space (a small “simulated” computer) for each program in memory.
Each time Windows jumps to the next program, the state of the current program is stored so that it can be brought back again when it is that program’s turn to receive some processor time. This includes processor register values and any data that might be overwritten by the next process.
Then, when the program comes around again in the time-slicing scheme, these values are restored into the processor registers, and program execution continues where it left off. This happens at a very low level, at the processor register level, and is handled by the Windows core.
切换程序在人角度开看,是非常迅速的,不必担心。
If this sounds like a wasteful use of processor cycles, you should be aware that during those few microseconds, the processor is able to run thousands of instructions. Modern processors already run at the gigaflop level, able to easily crunch a billion math calculations in a short “time slice.”
What this means is that the operating system has a very low-level core that manages the computer system. Preemptive抢占式 means that the operating system can preempt the functioning of a program, causing it to pause, and the operating system can then allow the program to start running again later.
When you have many programs and processes (each with one or more threads) begging for processor time, this is called a time-slicing system时间分片系统, which is how Windows works. As you might imagine, having a multi-processor system is a real advantage when you are using an operating system such as this.
多线程
Multi-threading is the process of breaking up a program into multiple threads, each of
which is like a separate program running. This is not the same as multi-tasking on the
system level 多任务不同于多线程. Multi-threading is sort of like multi-multi-tasking, where each program has running parts of its own, and those small program fragments are oblivious of the timeslicing system performed by the operating system.
As far as your main Windows program and all of its threads are concerned, they all have complete control over the system and have no “sense” that the operating system is slicing up the time allotted to each thread or process. Therefore, multi-threading means that each program is capable of delegating processes to its own mini-programs.
For instance, a chess program might create a thread to think ahead while the player is working on his next move. The “thought” thread would continue to update moves and counter-moves while waiting for the player. While this might just as easily be accomplished with a program loop that thinks while waiting for user input, the ability to delegate the process out to a thread might have significant benefits for a program.
Just as an example, you can create two threads in a Windows program and give each thread its own loop.
As far as each thread is concerned, its loop runs endlessly and it runs extremely fast, without interruption.
But at the system level, each thread is given a slice of processor time.
Depending on the speed of the processor and operating system, a thread may be interrupted 1,000 times per second, but the source code running in that thread will not be interrupted in any way.
Multi-threading is very useful for game programming.
多线程在游戏中的应用
The many tasks involved in a game loop might be delegated into separate threads that will execute independently, each one communicating with the main program.
A thread might be set up to handle screen updates automatically.
All the program would have to do then is update the double buffer with all of the objects on the screen, and the thread will do the work on a regular basis— perhaps even with timing built in so that the game will run at a uniform speed regardless of the processor.
All of the popular game engines today are multi-threaded (such as Unreal and Unity).
事件处理
At this point, you might be asking yourself, “How does Windows keep track of so many programs running at the same time?” Windows handles the problem,
- first of all, by requiring that programs be event-driven.
- Secondly, Windows uses system-wide messages to communicate.
Windows messages are small packets of data sent by the operating system to each running program with three primary features telling that program that some event has occurred:
- window handle,
- instance identifier,
- message type
The events will normally involve user input, such as a mouse click or key press, but might be from a communications port or a TCP/IP socket used by a networking library (which is used in multiplayer games).
Each Windows program must check every message that comes in through the message handler to determine whether the message is important. Messages that are not identified are sent along to the default message handler. (类比)Think of messages as fish—when you catch a fish that is too small or that you don’t like, you throw it back. But you keep the fish that you want.
It is similar in the Windows event-driven architecture; if your program recognizes a message that it wants to keep, that message is taken out of the message stream. Most of the messages will be key presses and mouse movement events (and they are still passed along even if you don’t need to use them).
(MyNote:个人认为它有点像寿司店回转寿司台)
Once you have experimented with Windows programming and have learned to handle some Windows messages, you will see how it was designed for applications, not games. The trick is learning to “tap into” the Windows messaging system and inject your own code, such as a DirectX initialization routine or a function call to refresh the screen.
All of the actions in a game are handled through the Windows messaging system; it is your job to intercept and deal with messages that are relevant to your game.
DirectX快速概览
We’re not going to begin writing any DirectX code just yet, but I do want you to see how it works with Windows.
DirectX provides an interface to the low-level hardware of your PC, providing a consistent一致的 and reliable set of functions for games that tap into the hardware (such as a video card).
DirectX is closely integrated into Windows and will not work with any other OS, as it relies on the basic libraries in the Windows API to function. Here is a rundown梗概 of the DirectX components:
-
Direct3D is the rendering library that provides access to the video card to render 2D and 3D graphics. This is the most important component.
-
DirectSound is the audio library used to play digital samples loaded from wave files with a multi-channel audio mixer.
-
XACT is a newer audio library that supplements DirectSound.
-
DirectInput is the device input library used to access keyboard, mouse, and joystick游戏杆 devices.
-
XInput is an input library that provides support for an Xbox 360 controller connected via wireless adapter or USB port.
-
DirectPlay is the old networking library that is no longer supported.
Direct3D是什么
Since Direct3D is the most important component of DirectX. Direct3D handles all of the 2D and 3D rendering in a game.
In later chapters, you will learn Direct3D beginning and how to load an image into memory as a texture and then draw the texture (in 2D mode), as well as apply the texture when rendering a 3D model.
You can still program a 2D game using Direct3D or use 2D sprites to enhance a 3D game. You will need to display information on the screen with a 2D font, so learning how to draw 2D graphics is a necessity必要的. For the short term, a brief overview of 2D textures and sprites will help you to understand Direct3D when we explore 3D programming later on.
**Our primary goal is to learn about 2D and 3D rendering necessary to program a game.**My intent is not to try to make you into an expert game programmer, just to give you
enough information (and enthusiasm!) to take yourself to the next level and learn more
about this subject.
We will eventually get into rendering 3D models with texturing and lighting, but that’s pretty advanced, so we’ll spend most of our time learning about 2D sprite-based games. Have fun with the material!
**Try not to get bogged down in the details!**不要过分陷于细节中 Because the details of 3D game programming are complex, the average一般 beginner’s eyes tend to glaze over when hearing about vertex buffers顶点缓冲 and texture coordinates纹理坐标.
I can relate, because it takes time to build up to details like that when you’re just getting started. Honestly, it’s better to program a great 2D sprite精灵 game than a dull 3D game that just isn’t fun.
Window程序基础
Every Windows program includes a function called WinMain at minimum. Most Windows programs also include a message handler function called WinProc that receives messages (such as key presses and mouse movement).
If you were writing a full-blown Windows application (for instance, a commercial software product like 3ds Max or Microsoft Word), then you would have a very large and complicated
WinProc function in the program to handle the many program states and events.
But in a DirectX program, you don’t really have to mess with events because your main
interest lies with the DirectX interfaces that provide their own functions for dealing with
events.
DirectX is mostly a polled轮询 SDK, in that you must ask for data rather than having it
thrown at you (which is the case with WinProc). For instance, when you start learning
about DirectInput, you’ll find that keyboard and mouse input is mainly gathered by calling functions to see what values have changed.
(MyNote:DirectX 消息接收者主动轮询才能得到信息。)
创建第一个Win32项目
项目实际上是一个管理程序中所有文件的文件。
Visual Studio Express 2013 for Windows Desktop 下载地址
安装过程按照其安装向导步骤安装即可。
1.菜单栏FILE->New Project
2.选择项目模板Installed,Visual C++,Win32->Win32 Project,并命名项目名,选后点击OK
3.接下来是设置向导Overview,点击Next
4.Application Settings中,勾选Empty project,不勾选Security Development Lifecycle(SDL)checks,点击Finish。
5.添加一cpp源文件,菜单栏PROJECT->Add New Item,创建命名为main.cpp源文件。
6.输入以下代码:
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nShowCmd)
{
MessageBox(NULL, "Welcome to Windows Programming!",
"HELLO WORLD", MB_OK | MB_ICONEXCLAMATION);
}
这个程序仅仅在屏幕上显示一个对话框。
一开始BUILD编译时抛出error C2664: 'int MessageBoxW(HWND,LPCWSTR,LPCWSTR,UINT)' : cannot convert argument 2 from 'const char [32]' to 'LPCWSTR'
Now let’s resolve this error.
It has to do with the character set, which can be either ANSI (8-bit) or Unicode (16-bit). Unicode is important for localization—a software engineering term that refers to converting the text in a program to other languages. Not all languages need Unicode characters, but some do—such as Mandarin and Japanese.
We could write all of our code to support Unicode character strings, with funky code鸡肋代码 that is not part of the C++ standard (like the infamous “L” character and TCHAR). But we want to write standards-compliant software without special codes.
解决方法:
菜单栏PROJECT-> XXXProperties->Configuration Properties->General->Project Defaults->Character Set 选择 Use Multi-Byte Character Set
编译运行后结果:
When you compile a C++ program with Visual Studio, the executable file(可执行文件exe) is usually written to a folder called Debug (inside your project’s folder).
理解WinMain
Every Windows program has a function called WinMain. WinMain is the Windows equivalent of the main function in console C++ programs, and is the initial entry point for a Windows program.
The most important function in your program will be WinMain, but after you have set up the messaging calls, you probably won’t come back to WinMain while working on other parts of the program.
The WinMain function hasn’t changed much since 16-bit Windows 3.0 back in 1991. WinMain is like the foreman工头 that tells the program what to work on. The job of WinMain is to set up the program, and then to set up the main message loop for the program. This loop processes all of the messages received by the program. Windows sends these messages to every running program.
Most of the messages will not be used by your program, and so the operating system doesn’t even send some messages to your program. Usually, WinMain will send messages over to another function called WinProc, which works closely with WinMain to process user input and other messages.
WinMain函数调用
int WINAPI WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow )
-
HINSTANCE hInstance
. The first parameter identifies the instance of the program
being called, as a program may be run several times. hInstance tells the program
which instance is trying to run. If the program is run more than once, the
general practice is to just close the new instance rather than running the
program again.(MyNote:这句看得不太懂。) -
HINSTANCE hPrevInstance
. The second parameter identifies the previous instance of
the program and is related to the first parameter. If hPrevInstance is NULL, then this
is the first instance of the program. -
LPTSTR lpCmdLine
. The third parameter is a string that contains the command-line
parameters passed to the program. This could be used to tell the program to use
certain options. -
int nCmdShow
. The last parameter specifies the options used when creating the
program window. 该参数可以是下列值之一:- SW_HIDE:隐藏窗口并且激活另外一个窗口。
- SW_MINIMIZE:最小化指定的窗口,并且激活在系统表中的顶层窗口。
- SW_RESTORE:激活并显示窗口。如果窗口已经最小化或最大化,系统将以恢复到原来的尺寸和位置显示窗口(与SW_SHOWNORMAL相同)。
- SW_SHOW:激活一个窗口并以原来的尺寸和位置显示窗口。
- SW_SHOWMAXIMIZED:激活窗口并且将其最大化。
- SW_SHOWMINIMIZED:激活窗口并将其目标化。
- SW_SHOWMINNOACTIVE:将一个窗口显示为图标。激活窗口维持活动状态。
- SW_SHOWNA:以窗口的当前状态显示窗口。激活窗口保持活动状态。
- SW_SHOWNOACTIVATE:以窗口的最近一次的尺寸和位置显示窗口。激活窗口维持激活状态。
- SW_SHOWNORMAL:激活并显示窗口。如果窗口最大化或最小化,系统将其恢复到原来的尺寸和位置(与SW_RESTORE相同)
You might have noticed that WinMain returns a value with the words int WINAPI in front of the function call. This is also standard practice and goes back to Windows 3.0.
A return value of zero indicates that the program never made it to the main loop and was terminated prematurely. Any non-zero value indicates success.
hInstance是程序的当前实例的句柄。在Windows这样的多任务操作系统中,一个程序可以同时运行多个实例。不同的实例间需要彼此区别,句柄就是干这个的。
HINSTANCE 实际是本模块在内存中的首地址。
程序的资源如菜单 对话框 字符 串 光标等等 还有函数 导入导出表等,都存储在本模块里。
所以要使用这些资源必须知道首地址,然后根据预定义的 找到各个资源 。
完整的WinMain
Listed below is more of a standard version of WinMain that you will often see in app code.
This is just an example, not a complete project.
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
//1. declare variables
//The MSG variable is used by the GetMessage function to retrieve the details of each Windows message.
MSG msg;
// register the class
// 还未介绍
MyRegisterClass(hInstance);
// initialize application
// **This code uses the hInstance variable passed to WinMain by Windows**. The variable is then passed on to the InitInstance function.
//InitInstance is located farther down in the program, and basically checks to see whether the program is already running and then creates the main program window.
if (!InitInstance (hInstance, nCmdShow)) return FALSE;
// main message loop
// The while loop in this part of WinMain will continue to run forever unless a message to kill the program comes along.
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
Even the simplest of graphics programs will need to process messages. Believe it or not, doing something as simple as printing “Hello World” on the screen requires that you wait for a message to come along for painting the screen.
Message handling does take some getting used to if you’re used to just calling a function when you need something (such as displaying text on the screen) done. (MyNote:不是”呼之即来挥之即去“易用性???)
GetMessage函数调用
-
LPMSG lpMsg
. This parameter is a pointer to a MSG structure that handles the message
information. -
HWND hWnd
. The second parameter is a handle to a specific window’s messages. If
NULL is passed, then GetMessage will return all of the messages for the current
instance of the program. -
UINT wMsgFilterMin
andUINT wMsgFilterMax
. These parameters tell GetMessage to
return messages in a certain range. The GetMessage call is the most crucial决定性的 line of code
in the entire Windows program! Without this single line in WinMain, your program
will be sensory-deprived感觉剥夺, unable to respond to the world.
The two core lines of code within the GetMessage loop work to process the message returned by GetMessage.
The Windows API Reference states that the TranslateMessage function is used to translate virtual-key messages into character messages, and then sent back through the Windows messaging system with DispatchMessage. These two functions will jointly set up the messages that you will expect to receive in WinProc (the window callback function) for your game window, such as WM_CREATE to create a window and WM_PAINT to draw the window.
I will cover WinProc in the next chapter. If you feel confused about Windows messaging, don’t worry about it, because this is just a precursor前导 to working with DirectX; once you have written a Windows message loop, you will not need to deal with it again, and you can focus on your DirectX code.
寻求帮助
If you need help with a Windows or DirectX function (such as Direct3DCreate9), highlight it in Visual Studio and press F1. This will bring up context-sensitive help in the default web browser by opening a Microsoft Developer Network web page with details about that function.