用Direct3D设计层峦叠嶂的效果
演示程序下载地址:http://download.youkuaiyun.com/detail/jiangcaiyang123/4206595
在制作二维游戏的时候,想要达到一种类似三维的那种效果。由于视锥的性质,在前面的事物看起来比较大,在后面的事物就比较小。一旦移动的时候在前面的事物移动得快,在后面的事物移动得要慢。简单地来说,就是那种层峦叠嶂的效果。怎么实现呢?我是从DirectX9开始学起的,不懂DirectDraw怎么实现,所以使用Direct3D来实现。其实深入地想想,诀窍就在纹理的UV上。之前的文章介绍了怎么使用Direct3D设计背景循环移动的效果(地址 http://blog.youkuaiyun.com/jiangcaiyang123/article/details/7254563),本文所要介绍的技术原理和那个非常相似。只是多贴了图而已。首先要明确几点。(这是针对对Direct3D有基础的同学们。)
嗯,这样说教也不太好,那么还是轻松一点地说吧。
在Direct3D上不是有多纹理渲染的例子吗?首先是否使用多纹理呢?当时我也是这么想的,但是考虑到如果使用多纹理的话,灵活顶点的数据结构(FVF)也就被硬编码了,不好更改。而且不好确定你要贴多少纹理至顶点上,如果你贴的纹理少了或者多了,你的代码结构也必须修改多处地方,所以这个方法不好。我们采用多次贴纹理的方法解决问题。在Direct3D中,可以复用顶点,多次调用IDirect3DDevice::SetTexture()方法,达到多次贴图的目的。
纹理的UV坐标是关键,有时候参数要多次调整才能达到效果。如果你对纹理的UV坐标有着深入的认识,那么制作出这样的效果应该不难吧。只需要稍微调整一下代码就可以达到效果。待会儿介绍。
首先呢,先使用面向过程的方法制作实验,代码的耦合度什么的,可以先不管他,达到了相应的效果的话,可以使用面向对象的方法把相关代码封装,这样就变得优雅了。
大家还没有看懂或是不明白所云的话,强烈建议下载我的演示程序看看,知道了这个效果的话,就知道了我接下来要叙述的内容了。
struct STVertex // 背景的结构体
{
STVertex( float _x, float _y, float _u, float _v );
void Set( float _x, float _y, float _u, float _v );
float x, y, z;
unsigned long color; // 颜色
float u, v; // 纹理内坐标
static const unsigned short FVF;
};
还是从顶点数据结构开始入手吧!这个顶点数据结构和以前的相差不大。就是添加了一些辅助的函数。这些辅助的函数是这样的:
STVertex::STVertex( float _x, float _y, float _u, float _v )
{
Set( _x, _y, _u, _v );
}
void STVertex::Set( float _x, float _y, float _u, float _v )
{
color = 0xFFFFFFFF;
x = _x, y = _y, z = 0.0f, u = _u, v = _v;
}
const unsigned short STVertex::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1;
然后记住,只建立一个顶点缓存,一个就好了,到时候修改顶点的XY和UV就可以了。随后建立一个类,管理背景图片的,它叫CBKGroundImage。下面是它的声明。
class CBKGroundImage // 背景图片的类
{
public:
explicit CBKGroundImage( IDirect3DTexture9* _pImage, float _moveU, float _moveV,
float _offsetU, float _offsetV, float _nU, float _nV );
void SetVerticesUV( STVertex* pVertices );
inline void MoveU( float scale ) // 移动U(X轴上移动)
{
m_OffsetU += m_MoveU * scale;
}
inline void MoveV( float scale ) // 移动V(Y轴上移动)
{
m_OffsetV += m_MoveV * -scale;
}
IDirect3DTexture9* m_pImage;
private:
float m_MoveU, m_MoveV;
float m_OffsetU, m_OffsetV;
float m_nU, m_nV;
};
我在每次渲染的时候,都设置纹理的UV,调用SetVerticesUV()就可以获得这样的效果。每次沿着X轴移动的时候,都会调用MoveU()函数,每次沿着X轴移动的时候,都会调用MoveV()函数。就这样纹理就移动了。由于我使用了矩阵转换,所以坐标系换成了我们中学学过的直角坐标系,就是屏幕客户区左下角为原点,向右向上递增。所以纹理要设置好世界矩阵。以下是我的关键代码列表:
// MultiTexture.h 多纹理作为背景进行渲染的头文件
// 2012-4-6 21:13:52 最后编辑
#ifndef _MULTITEXTURE_H_
#define _MULTITEXTURE_H_
#include <vector>
#include <d3dx9.h>
#include "../DX框架/Scenario.h"
#include "../DX框架/JCYInput.h"
#include "Font.h"
namespace NBackground
{
struct STVertex // 背景的结构体
{
STVertex( float _x, float _y, float _u, float _v );
void Set( float _x, float _y, float _u, float _v );
float x, y, z;
unsigned long color; // 颜色
float u, v; // 纹理内坐标
static const unsigned short FVF;
};
class CBKGroundImage // 背景图片的类
{
public:
explicit CBKGroundImage( IDirect3DTexture9* _pImage, float _moveU, float _moveV,
float _offsetU, float _offsetV, float _nU, float _nV );
void SetVerticesUV( STVertex* pVertices );
inline void MoveU( float scale ) // 移动U(X轴上移动)
{
m_OffsetU += m_MoveU * scale;
}
inline void MoveV( float scale ) // 移动V(Y轴上移动)
{
m_OffsetV += m_MoveV * -scale;
}
IDirect3DTexture9* m_pImage;
private:
float m_MoveU, m_MoveV;
float m_OffsetU, m_OffsetV;
float m_nU, m_nV;
};
}
class CMultiTexture: public IScenario
{
public:
CMultiTexture( void ); // 构造函数
~CMultiTexture( void ); // 析构函数
TCHAR* Draw( void ); // 绘制
inline TCHAR* GetName( void ) // 获取名字
{
return TEXT( "多纹理背景矩阵转换场景" );
}
unsigned long Release( void ); // 释放空间
VALIDATE_INVALIDATE_ENABLE
private:
JCY::CKeyBoard m_KeyBoard; // 键盘
LPFont m_pFont; // 字体
NBackground::STVertex *m_pVertices; // 顶点坐标
IDirect3DVertexBuffer9* m_pBuffer; // 顶点缓存
std::vector<NBackground::CBKGroundImage>
m_VecBKGroundImages; // 顶点缓存的向量
void SetVerticesXY( NBackground::STVertex* pVertices );
void AddNewBackground( const char* fileName );
};
#endif
// MultiTexture.cpp 多纹理作为背景进行渲染的实现文件
// 2012年4月6日21:15:55 最后编辑
#define USE_TSSTREAM
#include "MultiTexture.h"
#include "../DX框架/PreCompl.h"
#include "../界面代码/VertexBuffer.h"
#include "../界面代码/LoadTexture.h"
// --------------全局的屏幕的偏移量---------------
unsigned long g_OffsetX = 0;
unsigned long g_OffsetY = 0;
unsigned long g_MaxX = 640;
unsigned long g_MaxY = 480;
namespace NBackground
{
STVertex::STVertex( float _x, float _y, float _u, float _v )
{
Set( _x, _y, _u, _v );
}
void STVertex::Set( float _x, float _y, float _u, float _v )
{
color = 0xFFFFFFFF;
x = _x, y = _y, z = 0.0f, u = _u, v = _v;
}
const unsigned short STVertex::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1;
/*--------------------------------------------------------------------*/
CBKGroundImage::CBKGroundImage( IDirect3DTexture9* _pImage, float _moveU, float _moveV,
float _offsetU, float _offsetV, float _nU, float _nV )
{
m_pImage = _pImage;
m_MoveU = _moveU;
m_MoveV = _moveV;
m_OffsetU = _offsetU;
m_OffsetV = _offsetV;
m_nU = _nU;
m_nV = _nV;
}
void CBKGroundImage::SetVerticesUV( STVertex* pVertices )
{
pVertices[0].u = m_OffsetU, pVertices[0].v = m_OffsetV;
pVertices[1].u = m_OffsetU, pVertices[1].v = m_OffsetV - m_nU;
pVertices[2].u = m_OffsetU + m_nV, pVertices[2].v = m_OffsetV;
pVertices[3].u = m_OffsetU + m_nV, pVertices[3].v = m_OffsetV - m_nU;
}
/*--------------------------------------------------------------------*/
}
void CMultiTexture::SetVerticesXY( NBackground::STVertex* pVertices )
{
pVertices[0].x = 0.0f, pVertices[0].y = 0.0f;
pVertices[1].x = 0.0f, pVertices[1].y = float( g_Height );
pVertices[2].x = float( g_Width ), pVertices[2].y = 0.0f;
pVertices[3].x = float( g_Width ), pVertices[3].y = float( g_Height );
for ( unsigned int i = 0; i < 4; i++ )
{
pVertices[i].color = D3DCOLOR_XRGB( 255, 255, 255 );
}
}
void CMultiTexture::AddNewBackground( const char* fileName )
{
using namespace NBackground;
IDirect3DTexture9* tmpTexture = 0;
D3DXIMAGE_INFO info;
size_t scale = m_VecBKGroundImages.size( ) + 1; // 多纹理形成背景的时候要指定放大倍数效果才满意
float moveU = 0.0f, moveV = 0.0f;
float offsetU = 0.0f, offsetV = 0.0f;
float nU = 0.0f, nV = 0.0f;
// 载入纹理
LoadTexture( fileName, &tmpTexture, &info );
// 设置顶点的坐标
nU = float( g_Width ) / float( info.Width * scale );
nV = float( g_Height ) / float( info.Height * scale );
moveU = ( 1.0f - nU ) / float( g_Width );
moveV = ( 1.0f - nV ) / float( g_Height );
offsetU = float( g_OffsetX ) * moveU;
offsetV = float( info.Height * scale - g_OffsetY ) * moveV;
m_VecBKGroundImages.push_back( CBKGroundImage( tmpTexture,
moveU, moveV, offsetU, offsetV, nU, nV ) );
}
CMultiTexture::CMultiTexture( void ): m_KeyBoard( false )
{
using namespace NBackground;
// 清零
m_pBuffer = 0;
m_pVertices = 0;
m_VecBKGroundImages.clear( );
// 创建顶点缓存
CreateVertexBuffer( &m_pBuffer, (void**)&m_pVertices,
4, sizeof( STVertex ), STVertex::FVF );
// 设置纹理和顶点混合
g_pDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
g_pDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE ); // 混合的第二个参数为常量,可以改变
g_pDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
// 设置Alpha混合
g_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
g_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
g_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
// 设置关灯
g_pDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
// 设置渲染方式
g_pDevice->SetRenderState( D3DRS_POINTSPRITEENABLE, TRUE );
g_pDevice->SetRenderState( D3DRS_POINTSCALEENABLE, TRUE );
// 设置其它的一些参数
g_pDevice->SetStreamSource( 0, m_pBuffer, 0, sizeof( STVertex ) );
g_pDevice->SetFVF( STVertex::FVF );
// 载入纹理
char* textureNames[] =
{
"../图片/背景一.png",
"../图片/背景二.png",
"../图片/背景三.png",
};
for ( unsigned int i = 0; i < 3; i++ )
{
AddNewBackground( textureNames[i] );
}
// 设置矩阵
D3DXMATRIX matView, matProject;
D3DXMatrixTranslation( &matView, -float( g_Width / 2 ), -float( g_Height / 2 ), 0.0f );
g_pDevice->SetTransform( D3DTS_VIEW, &matView );
D3DXMatrixOrthoLH( &matProject, float( g_Width ), float( g_Height ), 0.0f, 100.0f );
g_pDevice->SetTransform( D3DTS_PROJECTION, &matProject );
// 设置顶点坐标
SetVerticesXY( m_pVertices );
// 初始化字体
m_pFont = new CFont;
m_pFont->LoadFromSystem( TEXT( "黑体" ), 40 );
m_pFont->SetPos( 0, 50 );
m_pFont->SetColor( D3DCOLOR_XRGB( 255, 255, 255 ) );
}
CMultiTexture::~CMultiTexture( void ) // 析构函数
{
Release( );
}
TCHAR* CMultiTexture::Draw( void ) // 绘制
{
using namespace NBackground;
g_pDevice->SetStreamSource( 0, m_pBuffer, 0, sizeof( STVertex ) );
g_pDevice->SetFVF( STVertex::FVF );
for ( size_t i = 0 ; i < m_VecBKGroundImages.size( ); i++ )
{
m_VecBKGroundImages[i].SetVerticesUV( m_pVertices );
g_pDevice->SetTexture( 0, m_VecBKGroundImages[i].m_pImage );
g_pDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );
}
/*
TSStream str;
str << TEXT( "offsetU的值是:" ) << m_VecBKGroundImages[0].offsetU
<< TEXT( "\noffsetV的值是:" ) << m_VecBKGroundImages[0].offsetV;*/
m_pFont->Display( TEXT( "By 蒋彩阳" ) );
// 按键响应
m_KeyBoard.Update( );
if ( m_KeyBoard.KeyDown( DIK_UP ) || m_KeyBoard.KeyDown( DIK_W ) )
{
if ( g_OffsetY < g_MaxY )
{
g_OffsetY++;
for ( size_t i = 0; i < m_VecBKGroundImages.size( ); i++ )
m_VecBKGroundImages[i].MoveV( 1.0f );
}
}
if ( m_KeyBoard.KeyDown( DIK_DOWN ) || m_KeyBoard.KeyDown( DIK_S ) )
{
if ( g_OffsetY > 0 )
{
g_OffsetY--;
for ( size_t i = 0; i < m_VecBKGroundImages.size( ); i++ )
m_VecBKGroundImages[i].MoveV( -1.0f );
}
}
if ( m_KeyBoard.KeyDown( DIK_LEFT ) || m_KeyBoard.KeyDown( DIK_A ) )
{
if ( g_OffsetX > 0 )
{
g_OffsetX--;
for ( size_t i = 0; i < m_VecBKGroundImages.size( ); i++ )
m_VecBKGroundImages[i].MoveU( -1.0f );
}
}
if ( m_KeyBoard.KeyDown( DIK_RIGHT ) || m_KeyBoard.KeyDown( DIK_D ) )
{
if ( g_OffsetX < g_MaxX )
{
g_OffsetX++;
for ( size_t i = 0; i < m_VecBKGroundImages.size( ); i++ )
m_VecBKGroundImages[i].MoveU( 1.0f );
}
}
return 0;
}
MAKE_RELEASE_IMPL( CMultiTexture ) // 释放空间
{
Invalidate( );
return 0;
}
MAKE_VALIDATE_IMPL( CMultiTexture )
{
return true;
}
MAKE_INVALIDATE_IMPL( CMultiTexture )
{
for ( size_t i = 0; i < m_VecBKGroundImages.size( ); i++ )
{
SAFE_RELEASE( m_VecBKGroundImages[i].m_pImage );
}
SAFE_RELEASE( m_pBuffer );
m_KeyBoard.Release( );
m_pFont->Release( );
SAFE_DELETE( m_pFont );
return true;
}
还没有完呢。我最近在制作一个地图编辑器,可以导出Lua脚本,其中使用了相关的技术,现在一并地展示出来:
使用方法:进入程序后,点击鼠标中键显示菜单,根据菜单来决定操作。在添加了游戏实体后,点击左键可以添加游戏单元,右键可以擦除游戏单元,可以重复在一块地方添加游戏单元。菜单中保存可以保存为Lua脚本。
演示程序下载地址:http://download.youkuaiyun.com/detail/jiangcaiyang123/4206576
有问题可以向我反馈:jiangcaiyang123@163.com