"Introduction to Game Programming 4th" Notes (1 / 14): Windows Preliminary

Windows Programming Overview

DirectX, a popular game programming library. It's easy to learn, hard to master.

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.

Get Windows

Software from older versions of Windows (except games, as PC specifications change rapidly) can run on newer versions of Windows without much modification.

Windows programming can be simple or complex.

Windows is an excellent multitasking operating system because it is message-driven .

Understanding Windows Messaging

External events, such as mouse clicks, can cause small electrical signals to be transferred from the mouse to the USB port and into the system bus. The Windows operating system picks up this signal from the system bus and generates a message to the running application (such as our game), which reacts to the message.

The computer's subconscious (the operating system that handles all the processing logic at all times) "presents" this event to the program, letting it know.

DirectX 9 legacy is still good only now popular Windows7.

Multitasking

Windows uses 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.


It is very fast to switch programs from a human perspective, so don't worry.

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.

Multithreading

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.

Application of multithreading in games

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).

event handling

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: I personally think it's a bit like a conveyor belt in a sushi restaurant)

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.

A quick overview of 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).

This figure shows how DirectX works with the Windows API.

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.

What is 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.

Windows Program Basics

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 message receivers actively poll to get information.)

Create your first Win32 project

A project is actually a file that manages all the files in the program.

Visual Studio Express 2013 for Windows Desktop download address

The installation process can be installed according to the steps of its installation wizard.

1. Menu bar FILE->New Project

2. Select the project template Installed, Visual C++, Win32->Win32 Project, and name the project name, click OK after selection

3. Next is the Setup Wizard Overview, click Next

4. In Application Settings, check Empty project, uncheck Security Development Lifecycle (SDL) checks, and click Finish.

5. Add a cpp source file, PROJECT->Add New Item in the menu bar, and create a source file named main.cpp.

6. Enter the following code:

#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);
}

This program just displays a dialog on the screen .

Throws when BUILD compiles at firsterror 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.

Workaround :

菜单栏PROJECT-> XXXProperties->Configuration Properties->General->Project Defaults->Character Set 选择 Use Multi-Byte Character Set

Result after compiling and running:

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).

Understanding 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.

A comparison of WinMain and WinProc

WinMain function call

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. The parameter can be one of the following values:
    • SW_HIDE: Hide the window and activate another window.
    • SW_MINIMIZE: Minimize the specified window and activate the top-level window in the system table.
    • SW_RESTORE: Activate and display the window. If the window has been minimized or maximized, the system will restore the window to its original size and position (same as SW_SHOWNORMAL).
    • SW_SHOW: Activate a window and display the window at its original size and position.
    • SW_SHOWMAXIMIZED: Activate the window and maximize it.
    • SW_SHOWMINIMIZED: Activate the window and target it.
    • SW_SHOWMINNOACTIVE: Show a window as an icon. The active window remains active.
    • SW_SHOWNA: Display the window in its current state. The activation window remains active.
    • SW_SHOWNOACTIVATE: Displays the window at its most recent size and position. The active window remains active.
    • SW_SHOWNORMAL: Activate and show the window. If the window is maximized or minimized, the system restores it to its original size and position (same as 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 is the handle to the current instance of the program. In a multitasking operating system like Windows, a program can run multiple instances at the same time. Different instances need to be distinguished from each other, and this is what the handle does.

Link

HINSTANCE is actually the first address of this module in memory.
Program resources such as menu dialog, string cursor, etc., as well as function import and export tables, are all stored in this module.
So to use these resources, you must know the first address, and then find each resource according to the predefined ones.

Link

Complete 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: not a "swipe away" ease of use ???)

GetMessage function call

  • 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 and UINT 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.

ask for help

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.

Guess you like

Origin blog.csdn.net/u011863024/article/details/120222510