初学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;
}
}
初学Direct X(4)的更多相关文章
- 初学Direct X(7) ——位图的旋转,缩放以及平移
初学Direct X(7) --位图的旋转,缩放以及平移 本文旨在实现通过D3DXMatrixTransformation2D函数实现位图的旋转,缩放以及平移操作,但是具体的原理部分会在后面进一步的探 ...
- 初学Direct X(10)—— D3D基础预备知识
初学Direct X(10) -- D3D基础预备知识 1. 像素格式 D3DFMT_X8R8G8B8(F) X:未加使用 8:8位用于显示 B:用于显示蓝色 F:浮点像素类型 以下三个较为常用,使用 ...
- 初学Direct X(9) ——文字的显示
初学Direct X(9) --文字的显示 本次学习如何使用ID3DXFont创建字体,使得我们可以在任何安装了Windows系统中TrueType字体来打印文字,不过最好使用标准字体,这样文字在每一 ...
- 初学Direct X(8) ——碰撞检测
初学Direct X(8) --碰撞检测 真正让一个游戏鹤立鸡群的是程序对碰撞的响应有多好,这里介绍两种检测的方法: 1) 基于边框的碰撞检测 2) 基于距离的碰撞检测 1. 基于边框的碰撞检测 1. ...
- 初学Direct X(6)
初学Direct X(6) 这一文本应和上一篇放在一起的,但是上一章写着写着发现对Draw绘制透明位图的方式有感觉了,决定就单写一篇,留作笔记了. 那这一篇是记录如何使用位图表来绘制动画帧,想象一下, ...
- 初学Direct X(5)
初学Direct X(5) 前面学习了使用表面绘制屏幕,但这种方法与另一种比较起来,有着绘图速度颇慢以及缺乏对任何透明类型的支持,这就是前面的篮框以及炸弹会有黑色背景的原因,这种方法就是纹理.他可以绘 ...
- 初学Direct X(3)
初学Direct X(3) 1.获取外设输入--键盘以及鼠标 无论是获取鼠标还是键盘的设备,首先得初始化DirectInput,不过先把必要的环境先配置好: 所要用到的头文件以及库文件是(相比于前两次 ...
- 初学Direct X (2)
初学Direct X (2) 这一次要学习如何现实位图,尽管看过对双缓冲机制还有很多疑问,但是这并不阻碍我对他的入门了解 Direct3D提供了一个双重/后台缓冲区,在调用CreateDevice之时 ...
- 初学DirectX(1)
初学Direct X (1) Direct3D设备用于访问视频卡的帧缓冲区,以及后台缓冲区.由于IDE是vs2013,默认安装了direct 9,只需要在使用头文件(1)并像使用库文件(2)即可 #i ...
随机推荐
- Sonar安装和常见问题解决
Sonar是一款代码质量分析工具,有助于帮助代码质量提高. Sonar的官网地址为:https://www.sonarqube.org/downloads/ 点击 Show All Versions ...
- Win32线程——优先权
<Win32多线程程序设计>–Jim Beveridge & Robert Wiener Win32 优先权是以数值表现的,并以进程的“优先权类别(priority class)” ...
- 【转载】对C#DateTime的一些扩展,计算周内第一天,最后一天
/// <summary> /// DateTime的一些扩展 /// </summary> public class DateTime2 { /// <summary& ...
- STM32之FSMC
FSMC全称“静态存储器控制器”. 使用FSMC控制器后,可以把FSMC提供的FSMC_A[25:0]作为地址线,而把FSMC提供的FSMC_D[15:0]作为数据总线. (1)当存储数据设为8位时, ...
- 团体队列 UVA540 Team Queue
题目描述 有t个团队的人正在排一个长队.每次新来一个人时,如果他有队友在排队,那么新人会插队到最后一个队友的身后.如果没有任何一个队友排队,则他会被排到长队的队尾. 输入每个团队中所有队员的编号,要求 ...
- Kali Basic Configuration
1:Kali Version root@kali-node01:~# cat /etc/os-release PRETTY_NAME="Kali GNU/Linux Rolling" ...
- webpack-dev-server 多入口自动刷新,支持对象
万物的来源~webpack 本身 watch webpack watch 传送门 webpack 可以监听文件变化,当它们修改后会重新编译 watch boolean 启用 Watch 模式.这意味着 ...
- 【Spark】Spark核心之弹性分布式数据集RDD
1. RDD概述 1.1 什么是RDD (1) RDD(Resilient Distributed Dataset)弹性分布式数据集,它是Spark的基本数据抽象,它代表一个不可变.可分区.里面的元素 ...
- 7z 压缩解压简单示例
7z命令行压缩示例: 7z -tZip a test.zip ./test/* -mx0 把test文件夹中所有文件以存储压缩的模式压缩成zip格式的文件,压缩文件为test.zip a为添加选项 ...
- scala 获取当前时间的两种方式
在编写程序时,有时需要获取当前时间,这在记录异常信息.获取程序运行耗时很有用处 方式一: val time1=System.currentTimeMillis() 这种方式获取的是程序运行到此的毫秒数 ...