用Direct3D设计层峦叠嶂的效果

本文详细介绍了如何利用Direct3D技术实现二维游戏中类似三维的层峦叠嶂效果,通过调整纹理UV坐标,实现背景在X轴上的平移,模拟远近视觉差异。此外,还展示了在制作地图编辑器时应用该技术,通过键盘操作调整视角,增强了用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值