zoukankan      html  css  js  c++  java
  • 初学Direct X(4)

    初学Direct X(4)


    本文学着做出一个如下的小游戏

    游戏方式是使用键盘控制红色的Bucket收集蓝色的炸弹

    1.酝酿一下

    现在我已经掌握:

    将位图文件加载到内存
    绘制位图到buckbuffer
    获取外设输入

    再来几个,获取表面的信息,例如宽和高

    D3DSURFACE_DESC desc;
    source->GetDesc(&desc);
    

    以及获取位图的信息,例如宽和高

    D3DXIMAGE_INFO image_info;
    HRESULT result = D3DXGetImageInfoFromFile(filename.c_str(),&image_info);
    

    2.程序的文件结构

    要成为专业的程序员,代码重用是很关键的。不可能一次次的重写代码,根据之前的经验可以把文件分布结构设如下:

    bomb.bmp
    bucket.bmp
    MyWindows.cpp Windows代码,包括WinMain和WinProc
    MyDirectX.h   DirectX变量和函数定义
    MyDirectX.cpp DirectX变量和函数实现
    MyGame.cpp    游戏的源代码
    

    接下来分类讲解

    3. MyWindows.cpp

    这里面就是写了很多遍的WinMain函数和WinProc函数了,其主要就是实现在Windows下窗口的注册和消息分发,以及屏幕的重绘,源码就不贴了

    4.MyDirectX.h

    #pragma once
    
    #define WIN32_EXTRA_LEAN
    #define DIRECTINPUT_VERSION 0x0800
    
    #include <windows.h>
    #include <d3d9.h>
    #include <d3dx9.h>
    #include <dinput.h>
    #include <ctime>
    #include <iostream>
    #include <iomanip>
    #include <sstream>
    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")
    
    // program values
    extern const string APPTITLE;
    extern const int SCREENW;
    extern const int SCREENH;
    extern bool gameover;
    
    // Direct3D object
    extern LPDIRECT3D9 d3d;
    extern LPDIRECT3DDEVICE9 d3ddev;
    extern LPDIRECT3DSURFACE9 backbuffer;
    
    // Direct3d functions
    bool Direct3D_Init(HWND window, int width, int height, bool fullscreen);
    void Direct3D_Shutdown();
    LPDIRECT3DSURFACE9 LoadSurface(string filename);
    // 整个source画到dest的(x,y)上
    void DrawSurface(LPDIRECT3DSURFACE9 dest,float x,float y,LPDIRECT3DSURFACE9 source);
    
    // DirectInput objects,device and states
    extern LPDIRECTINPUT dinput;
    extern LPDIRECTINPUTDEVICEA dimouse;
    extern LPDIRECTINPUTDEVICEA dikeyboard;
    extern DIMOUSESTATE mouse_state;
    extern char keys[256];
    
    // DirectInput functions
    bool DirectInput_Init(HWND window);
    void DirectInput_Update();
    void DirectInput_Shutdown();
    int Key_Down(int key);
    
    // Game functions
    bool Game_Init(HWND window);
    void Game_Run(HWND window);
    void Game_End();
    

    5.MyDirectX.cpp

    这里是关于Direct3D的实现,包括初始化Direct3D,以及创建设备,还有输入设备的初始化。还有加载位图与绘制位图的函数实现,关于加载位图以及创建离屏表面的操作已经在前面讲过了,这里看看与位图相关的操作吧

    5.1 加载指定位图到表面上,实现了一次性完成了将位图加载到表面上

    LPDIRECT3DSURFACE9 LoadSurface(string filename){
    	LPDIRECT3DSURFACE9 image = NULL;
    	D3DXIMAGE_INFO image_info;
    	HRESULT result = D3DXGetImageInfoFromFile(filename.c_str(),&image_info);
    	if (result != D3D_OK) return NULL;
    	
    	d3ddev->CreateOffscreenPlainSurface(
    		image_info.Width,
    		image_info.Height,
    		D3DFMT_X8R8G8B8,
    		D3DPOOL_DEFAULT,
    		&image,
    		NULL
    		);
    	if (result != D3D_OK) return NULL;
    	result = D3DXLoadSurfaceFromFile(
    		image,
    		NULL,
    		NULL,
    		filename.c_str(),
    		NULL,
    		D3DX_DEFAULT,
    		D3DCOLOR_XRGB(0,0,0),
    		NULL
    		);
    	if (result != D3D_OK) return NULL;
    	return image;
    }
    

    5.2 将某表面全部加载到指定表面的指定位置

    实现简单,调用StretchRect即可,但需要使用GetDesc获取源表面的尺寸

    void DrawSurface(LPDIRECT3DSURFACE9 dest,float x,float y,LPDIRECT3DSURFACE9 source){
    	D3DSURFACE_DESC desc;
    	source->GetDesc(&desc);
    	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 };
    	d3ddev->StretchRect(source, &source_rect,dest,&dest_rect,D3DTEXF_NONE);
    }
    

    5.3 获取外设输入

    这个函数我觉得挺有意思的,注意到获取输入并不在游戏的主循环里,而是可以将获取的输入保存到了一个变量里,可供后面的程序使用,之前看到这儿的时候,并没有意识到这个点

    void DirectInput_Update(){
    	dimouse->GetDeviceState(sizeof(mouse_state),&mouse_state);
    	dikeyboard->GetDeviceState(sizeof(keys),&keys);
    }
    

    6. MyGame.cpp

    这里就是游戏的逻辑部分了,现在假设只有一个炸弹和一个篮框
    对于他们,需要设计一个结构体
    例如下面设个是炸弹的,根据游戏方式,在接到了一个炸弹之后,需要生成另一个炸弹,故需要一个reset函数来重置x,y坐标,由于炸弹位图的宽是128,避免超出显示范围,需要减去一个128

    struct BOMB{
    	float x, y;
    	void reset(){
    		x = (float)(rand() % (SCREENW - 128));
    		y = 0;
    	}
    };
    

    篮框的就简单多了,他并不需要多么复杂的控制,只需要简单的平面移动即可,据此设计如下

    struct BUCKET{
    	float x, y;
    }; `
    

    下面进入游戏的逻辑控制,也就是Game_Run()
    首先检测Dircet3D设备是否还存在:

    if (!d3ddev) return;
    

    接着获取玩家的输入,将玩家的输入都存进了变量之中:

    dimouse->GetDeviceState(sizeof(mouse_state),&mouse_state);
    dikeyboard->GetDeviceState(sizeof(keys),&keys);
    

    由于每次windows消息循环Game_Run()都会被调用,别忘了控制炸弹的运动,使其在y方向上下降:

    bomb.y += 2.0f;
    

    如果没接着炸弹会就gg了:

    if (bomb.y > SCREENH){
    	MessageBox(window,"Oh,no,the bomb exploded","YOU STINK",0);
    	gameover = true;
    }
    

    如果没gg,那就根据前面的玩家输入来控制篮框吧,move_pixel是移动的距离值

    if (Key_Down(DIK_W)){
    	bucket.y -= move_pixel;
    }
    if (Key_Down(DIK_A)){
    	bucket.x -= move_pixel;
    }
    if (Key_Down(DIK_S)){
    	bucket.y += move_pixel;
    }
    if (Key_Down(DIK_D)){
    	bucket.x += move_pixel;
    }
    

    在移动篮筐之时,超出屏幕就不好了,所以我们需要保持篮框一直在窗口里面:

    if (bucket.x < 0)
    	bucket.x = 0;
    if (bucket.x > SCREENW - 128)
    	bucket.x = SCREENW - 128;
    if (bucket.y< 0)
    	bucket.y = 0;
    if (bucket.y > SCREENH - 128)
    	bucket.y = SCREENH - 128;
    

    篮框移动,炸弹下落,一切看起来很不错,接下里就需要判断碰撞了,只需要判断炸弹是否在篮框内部就行了,当然还得做出反应,例如刷新分数以及重置炸弹的位置:

    //接到一半也算吧:)
    int cx = (int)(bomb).x + 64;
    int cy = (int)(bomb).y + 64;
    if (cx > bucket.x && cx < bucket.x + 128 &&
    	cy > bucket.y && cy < bucket.y + 128){
    	score++;
    	std::ostringstream os;
    	os << APPTITLE << " score: " << score;
    	string scoreSr = os.str();
    	SetWindowText(window,scoreSr.c_str());
    	(bomb).reset();
    }
    

    逻辑控制完成,接下来让我们把这一切都绘制到屏幕上吧,首先把backbuffer填充一下颜色,为显示做准备:

    d3ddev->ColorFill(backbuffer, NULL, D3DCOLOR_XRGB(0, 0, 0));
    

    接下来就可以把他们的位置直接放到屏幕上了:

    if (d3ddev->BeginScene()){
    	DrawSurface(backbuffer, bomb.x, bomb.y, bomb_surf);
    	DrawSurface(backbuffer, bucket.x, bucket.y, bucket_surf);
    
    	d3ddev->EndScene();
    	d3ddev->Present(NULL,NULL,NULL,NULL);
    }
    

    别忘了留一个优雅的退出方式:

    if (Key_Down(DIK_ESCAPE)){
    	gameover = true;
    }
    

    6.1 Game_Run()完整代码

    void Game_Run(HWND window){
    	if (!d3ddev) return;
    
    	// 获取键盘输入
    	DirectInput_Update();
    
    	// 炸弹的运动
    	BombMove(&bomb);
    
    	// 判断炸弹没接到
    	BombOutOfRange(window,&bomb);
    
    	// 根据键盘移动物体
    	MovedByKeyBoard();
    
    	//保持bucket在屏幕里
    	KeepInScreen();
    
    	// 碰撞检测
    	CheckCollide(window,&bomb);
    
    	d3ddev->ColorFill(backbuffer, NULL, D3DCOLOR_XRGB(0, 0, 0));
    	if (d3ddev->BeginScene()){
    		DrawSurface(backbuffer, bomb.x, bomb.y, bomb_surf);
    		DrawSurface(backbuffer, bucket.x, bucket.y, bucket_surf);
    
    		d3ddev->EndScene();
    		d3ddev->Present(NULL,NULL,NULL,NULL);
    	}
    
    	if (Key_Down(DIK_ESCAPE)){
    		gameover = true;
    	}
    
    }
    
  • 相关阅读:
    如何使用谷歌的网页删除请求工具?
    已有记录表添加特定排序主键ID
    用ASP实现超长内容的word模板替换objDoc.Content.Find.Execute
    内网SMTP发送失败的曲线救国之策
    IIS无法在后台生成WORD文件的故障
    WINDOWS2003进行WindowsFTP设置的一些注意事项
    解决IISASP调用XmlHTTP出现msxml3.dll (0x80070005) 拒绝访问的错误
    [转]Cate:我是如何准备Google面试的
    Ubuntu的启动配置文件grub.cfg(menu.lst)设置指南
    Linux启动过程详解
  • 原文地址:https://www.cnblogs.com/leihui/p/8908728.html
Copyright © 2011-2022 走看看