本系列文章由 net_assassin 整理编写,转载请注明出处。
http://blog.csdn.net/net_assassin/article/category/1100363
作者:net_assassin 邮箱: net_assassin@hotmail.com 期待着与志同道合的朋友们相互交流
前面讲到了如何从键盘和鼠标获取输入,这一章我们利用前面学到知识制作我们的游戏来演示到目前为止我们所学的一切。
这个游戏要求用户在屏幕底部移动一个篮筐,抓住从顶部随机落下的。
重点:我们将 Windows 、 DirectX 和 游戏源代码分开存放在不同文件中。
MyWindows.cpp 所有Windows代码,包括WinMain 和 WinProc
MyDirectX.h DirectX变量和函数定义
MyDirectX.cpp DirectX变量和函数实现
MyGame.cpp 游戏的源代码
要成为专业程序员,代码重用是关键。不能一次一次重写代码并指望每一次都能把工作做好。
到目前为止我们所创建的源代码文件提供了一个可极大减少编写Windows/DirectX游戏所需工作量的游戏框架。
效果图:
贴代码:
MyWindows.cpp
#include "MyDirectX.h"
using namespace std;
bool gameover = false;
/**
** Windows event handler
**/
LRESULT WINAPI WinProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_DESTROY:
gameover = true;
PostQuitMessage(0);
return 0;
}
return DefWindowProc( hWnd, msg, wParam, lParam );
}
/**
** Windows entry point
**/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
//initialize window settings
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)WinProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = APPTITLE.c_str();
wc.hIconSm = NULL;
RegisterClassEx(&wc);
//create a new window
HWND window = CreateWindow( APPTITLE.c_str(), APPTITLE.c_str(),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
SCREENW, SCREENH, NULL, NULL, hInstance, NULL);
if (window == 0) return 0;
//display the window
ShowWindow(window, nCmdShow);
UpdateWindow(window);
//initialize the game
if (!Game_Init(window)) return 0;
// main message loop
MSG message;
while (!gameover)
{
if (PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
//process game loop
Game_Run(window);
}
//shutdown
Game_End();
return message.wParam;
}
MyDirectX.h
#pragma once
//header files
#define WIN32_EXTRA_LEAN
#define DIRECTINPUT_VERSION 0x0800
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <dinput.h>
#include <xinput.h>
#include <ctime>
#include <iostream>
#include <iomanip>
using namespace std;
//libraries
#pragma comment(lib,"winmm.lib")
#pragma comment(lib,"user32.lib")
#pragma comment(lib,"gdi32.lib")
#pragma comment(lib,"dxguid.lib")
#pragma comment(lib,"d3d9.lib")
#pragma comment(lib,"d3dx9.lib")
#pragma comment(lib,"dinput8.lib")
#pragma comment(lib,"xinput.lib")
//program values
extern const string APPTITLE;
extern const int SCREENW;
extern const int SCREENH;
extern bool gameover;
//Direct3D objects
extern LPDIRECT3D9 d3d;
extern LPDIRECT3DDEVICE9 d3ddev;
extern LPDIRECT3DSURFACE9 backbuffer;
//Direct3D functions
bool Direct3D_Init(HWND hwnd, int width, int height, bool fullscreen);
void Direct3D_Shutdown();
LPDIRECT3DSURFACE9 LoadSurface(string filename);
void DrawSurface(LPDIRECT3DSURFACE9 dest, float x, float y, LPDIRECT3DSURFACE9 source);
//DirectInput objects, devices, and states
extern LPDIRECTINPUT8 dinput;
extern LPDIRECTINPUTDEVICE8 dimouse;
extern LPDIRECTINPUTDEVICE8 dikeyboard;
extern DIMOUSESTATE mouse_state;
extern XINPUT_GAMEPAD controllers[4];
//DirectInput functions
bool DirectInput_Init(HWND);
void DirectInput_Update();
void DirectInput_Shutdown();
int Key_Down(int);
int Mouse_Button(int);
int Mouse_X();
int Mouse_Y();
void XInput_Vibrate(int contNum = 0, int amount = 65535);
bool XInput_Controller_Found();
//game functions
bool Game_Init(HWND window);
void Game_Run(HWND window);
void Game_End();
MyDirectX.cpp
#include "MyDirectX.h"
#include <iostream>
using namespace std;
//Direct3D variables
LPDIRECT3D9 d3d = NULL;
LPDIRECT3DDEVICE9 d3ddev = NULL;
LPDIRECT3DSURFACE9 backbuffer = NULL;
//DirectInput variables
LPDIRECTINPUT8 dinput = NULL;
LPDIRECTINPUTDEVICE8 dimouse = NULL;
LPDIRECTINPUTDEVICE8 dikeyboard = NULL;
DIMOUSESTATE mouse_state;
char keys[256];
XINPUT_GAMEPAD controllers[4];
/**
** Direct3D initialization
**/
bool Direct3D_Init(HWND window, int width, int height, bool fullscreen)
{
//initialize Direct3D
d3d = Direct3DCreate9(D3D_SDK_VERSION);
if (!d3d) return false;
//set Direct3D presentation parameters
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = (!fullscreen);
d3dpp.SwapEffect = D3DSWAPEFFECT_COPY;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.BackBufferCount = 1;
d3dpp.BackBufferWidth = width;
d3dpp.BackBufferHeight = height;
d3dpp.hDeviceWindow = window;
//create Direct3D device
d3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window,
D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3ddev);
if (!d3ddev) return false;
//get a pointer to the back buffer surface
d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);
return true;
}
/**
** Direct3D shutdown
**/
void Direct3D_Shutdown()
{
if (d3ddev) d3ddev->Release();
if (d3d) d3d->Release();
}
/**
** Draws a surface to the screen using StretchRect
**/
void DrawSurface(LPDIRECT3DSURFACE9 dest, float x, float y, LPDIRECT3DSURFACE9 source)
{
//get width/height from source surface
D3DSURFACE_DESC desc;
source->GetDesc(&desc);
//create rects for drawing
RECT source_rect = {0, 0, (long)desc.Width, (long)desc.Height };
RECT dest_rect = { (long)x, (long)y, (long)x+desc.Width, (long)y+desc.Height};
//draw the source surface onto the dest
d3ddev->StretchRect(source, &source_rect, dest, &dest_rect, D3DTEXF_NONE);
}
/**
** Loads a bitmap file into a surface
**/
LPDIRECT3DSURFACE9 LoadSurface(string filename)
{
LPDIRECT3DSURFACE9 image = NULL;
//get width and height from bitmap file
D3DXIMAGE_INFO info;
HRESULT result = D3DXGetImageInfoFromFile(filename.c_str(), &info);
if (result != D3D_OK)
return NULL;
//create surface
result = d3ddev->CreateOffscreenPlainSurface(
info.Width, //width of the surface
info.Height, //height of the surface
D3DFMT_X8R8G8B8, //surface format
D3DPOOL_DEFAULT, //memory pool to use
&image, //pointer to the surface
NULL); //reserved (always NULL)
if (result != D3D_OK) return NULL;
//load surface from file into newly created surface
result = D3DXLoadSurfaceFromFile(
image, //destination surface
NULL, //destination palette
NULL, //destination rectangle
filename.c_str(), //source filename
NULL, //source rectangle
D3DX_DEFAULT, //controls how image is filtered
D3DCOLOR_XRGB(0,0,0), //for transparency (0 for none)
NULL); //source image info (usually NULL)
//make sure file was loaded okay
if (result != D3D_OK) return NULL;
return image;
}
/**
** DirectInput initialization
**/
bool DirectInput_Init(HWND hwnd)
{
//initialize DirectInput object
HRESULT result = DirectInput8Create(
GetModuleHandle(NULL),
DIRECTINPUT_VERSION,
IID_IDirectInput8,
(void**)&dinput,
NULL);
//initialize the keyboard
dinput->CreateDevice(GUID_SysKeyboard, &dikeyboard, NULL);
dikeyboard->SetDataFormat(&c_dfDIKeyboard);
dikeyboard->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
dikeyboard->Acquire();
//initialize the mouse
dinput->CreateDevice(GUID_SysMouse, &dimouse, NULL);
dimouse->SetDataFormat(&c_dfDIMouse);
dimouse->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
dimouse->Acquire();
d3ddev->ShowCursor(false);
return true;
}
/**
** DirectInput update
**/
void DirectInput_Update()
{
//update mouse
dimouse->GetDeviceState(sizeof(mouse_state), (LPVOID)&mouse_state);
//update keyboard
dikeyboard->GetDeviceState(sizeof(keys), (LPVOID)&keys);
//update controllers
for (int i=0; i< 4; i++ )
{
ZeroMemory( &controllers[i], sizeof(XINPUT_STATE) );
//get the state of the controller
XINPUT_STATE state;
DWORD result = XInputGetState( i, &state );
//store state in global controllers array
if (result == 0) controllers[i] = state.Gamepad;
}
}
/**
** Return mouse x movement
**/
int Mouse_X()
{
return mouse_state.lX;
}
/**
** Return mouse y movement
**/
int Mouse_Y()
{
return mouse_state.lY;
}
/**
** Return mouse button state
**/
int Mouse_Button(int button)
{
return mouse_state.rgbButtons[button] & 0x80;
}
/**
** Return key press state
**/
int Key_Down(int key)
{
return (keys[key] & 0x80);
}
/**
** DirectInput shutdown
**/
void DirectInput_Shutdown()
{
if (dikeyboard)
{
dikeyboard->Unacquire();
dikeyboard->Release();
dikeyboard = NULL;
}
if (dimouse)
{
dimouse->Unacquire();
dimouse->Release();
dimouse = NULL;
}
}
/**
** Returns true if controller is plugged in
**/
bool XInput_Controller_Found()
{
XINPUT_CAPABILITIES caps;
ZeroMemory(&caps, sizeof(XINPUT_CAPABILITIES));
XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps);
if (caps.Type != 0) return false;
return true;
}
/**
** Vibrates the controller
**/
void XInput_Vibrate(int contNum, int amount)
{
XINPUT_VIBRATION vibration;
ZeroMemory( &vibration, sizeof(XINPUT_VIBRATION) );
vibration.wLeftMotorSpeed = amount;
vibration.wRightMotorSpeed = amount;
XInputSetState( contNum, &vibration );
}
MyGame.cpp
#include "MyDirectX.h"
using namespace std;
const string APPTITLE = "Bomb Catcher Game";
const int SCREENW = 1024;
const int SCREENH = 768;
LPDIRECT3DSURFACE9 bomb_surf = NULL;
LPDIRECT3DSURFACE9 bucket_surf = NULL;
struct BOMB
{
float x,y;
void reset()
{
x = (float)(rand() % (SCREENW-128));
y = 0;
}
};
BOMB bomb;
struct BUCKET
{
float x,y;
};
BUCKET bucket;
int score = 0;
int vibrating = 0;
/**
** Game initialization
**/
bool Game_Init(HWND window)
{
Direct3D_Init(window, SCREENW, SCREENH, false);
DirectInput_Init(window);
bomb_surf = LoadSurface("bomb.bmp");
if (!bomb_surf) {
MessageBox(window, "Error loading bomb","Error",0);
return false;
}
bucket_surf = LoadSurface("bucket.bmp");
if (!bucket_surf) {
MessageBox(window, "Error loading bucket","Error",0);
return false;
}
//position the bomb
srand( (unsigned int)time(NULL) );
bomb.reset();
//position the bucket
bucket.x = 500;
bucket.y = 630;
return true;
}
void Game_Run(HWND window)
{
//make sure the Direct3D device is valid
if (!d3ddev) return;
//update input devices
DirectInput_Update();
//move the bomb down the screen
bomb.y += 2.0f;
//see if bomb hit the floor
if (bomb.y > SCREENH)
{
MessageBox(0,"Oh no, the bomb exploded!!","YOU STINK",0);
gameover = true;
}
//move the bucket with the mouse
int mx = Mouse_X();
if (mx < 0) bucket.x -= 6.0f;
else if (mx > 0) bucket.x += 6.0f;
//move the bucket with the keyboard
if (Key_Down(DIK_LEFT)) bucket.x -= 6.0f;
else if (Key_Down(DIK_RIGHT)) bucket.x += 6.0f;
//move the bucket with the controller
if (XInput_Controller_Found())
{
//left analog thumb stick
if (controllers[0].sThumbLX < -5000)
bucket.x -= 6.0f;
else if (controllers[0].sThumbLX > 5000)
bucket.x += 6.0f;
//left and right triggers
if (controllers[0].bLeftTrigger > 128)
bucket.x -= 6.0f;
else if (controllers[0].bRightTrigger > 128)
bucket.x += 6.0f;
//left and right D-PAD
if (controllers[0].wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER)
bucket.x -= 6.0f;
else if (controllers[0].wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER)
bucket.x += 6.0f;
//left and right shoulders
if (controllers[0].wButtons & XINPUT_GAMEPAD_DPAD_LEFT)
bucket.x -= 6.0f;
else if (controllers[0].wButtons & XINPUT_GAMEPAD_DPAD_RIGHT)
bucket.x += 6.0f;
}
//update vibration
if (vibrating > 0)
{
vibrating++;
if (vibrating > 20)
{
XInput_Vibrate(0, 0);
vibrating = 0;
}
}
//keep bucket inside the screen
if (bucket.x < 0) bucket.x = 0;
if (bucket.x > SCREENW-128) bucket.x = SCREENW-128;
//see if bucket caught the bomb
int cx = bomb.x + 64;
int cy = bomb.y + 64;
if (cx > bucket.x && cx < bucket.x+128 && cy > bucket.y && cy < bucket.y+128)
{
//update and display score
score++;
char s[255];
sprintf(s, "%s [SCORE %d]", APPTITLE.c_str(), score);
SetWindowText(window, s);
//vibrate the controller
XInput_Vibrate(0, 65000);
vibrating = 1;
//restart bomb
bomb.reset();
}
//clear the backbuffer
d3ddev->ColorFill(backbuffer, NULL, D3DCOLOR_XRGB(0,0,0));
//start rendering
if (d3ddev->BeginScene())
{
//draw the bomb
DrawSurface(backbuffer, bomb.x, bomb.y, bomb_surf);
//draw the bucket
DrawSurface(backbuffer, bucket.x, bucket.y, bucket_surf);
//stop rendering
d3ddev->EndScene();
d3ddev->Present(NULL, NULL, NULL, NULL);
}
//escape key exits
if (Key_Down(DIK_ESCAPE))
gameover = true;
//controller Back button also exits
if (controllers[0].wButtons & XINPUT_GAMEPAD_BACK)
gameover = true;
}
void Game_End()
{
if (bomb_surf) bomb_surf->Release();
if (bucket_surf) bucket_surf->Release();
DirectInput_Shutdown();
Direct3D_Shutdown();
}