A星算法和双向A星(修正)-附源码

A星算法和双向A星

实现效果

A星寻路
请添加图片描述
双向A星
请添加图片描述

介绍

基于VS2017 MFC Direct3D9实现的A星寻路
在这里插入图片描述
什么也不说就是干!
用mfc创建单文档框架,不选文档视图结构,生成代码,程序逻辑封装到AStarGame里面。主要在CChildView里面操作。大体结构就是AStarApp里面有一个主框架CMainFrame主框架里面有一个CChildView,视图里面有一个游戏AStarGame,游戏里面有一个GameObjectManager,游戏管理有一个AStarMap,地图里面是主要的寻路逻辑。

AStarApp和CMainFrame介绍

一行代码没改

CChildView介绍

这个里面声明AStarGame变量,这里有一个关键点,创建窗口的时候窗口大小是0,无法创建设备需要将创建设备的逻辑放到OnSize函数里面,第一次进入这个函数是创建的时候,第二次是主框架布局的时候,这个时候的大小是确定的。

void CChildView::OnSize(UINT nType, int cx, int cy)
{
   
	CWnd::OnSize(nType, cx, cy);
	// TODO: 在此处添加消息处理程序代码
	if (cx > 0 && cy > 0)
	{
   
		if (!m_pGame->GameInited())
		{
   
			m_pGame->Init(m_hWnd);
		}
	}
}

视图类主要是将MFC框架的事件传入到游戏逻辑里面,以驱动游戏逻辑。视图更新是用定时器驱动游戏渲染的。


// ChildView.cpp: CChildView 类的实现
//

#include "pch.h"
#include "framework.h"
#include "AStarApp.h"
#include "ChildView.h"
#include "AStarViewport.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CChildView

CChildView::CChildView()
{
   
	m_pGame = new	 CAStarGame();
}

CChildView::~CChildView()
{
   
	AStarDelete(m_pGame);
}

BEGIN_MESSAGE_MAP(CChildView, CWnd)
	ON_WM_PAINT()
	ON_WM_CREATE()
	ON_WM_LBUTTONDOWN()
	ON_WM_RBUTTONDOWN()
	ON_WM_TIMER()
	ON_WM_SIZE()
	ON_WM_KEYDOWN()
END_MESSAGE_MAP()



// CChildView 消息处理程序

BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs) 
{
   
	if (!CWnd::PreCreateWindow(cs))
		return FALSE;

	cs.dwExStyle |= WS_EX_CLIENTEDGE;
	cs.style &= ~WS_BORDER;
	cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, 
		::LoadCursor(nullptr, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), nullptr);

	return TRUE;
}

D3DXVECTOR2 CChildView::GetViewportPoint(CPoint point)
{
   
	RECT rt;
	GetClientRect(&rt);

	float width = (float)(rt.right - rt.left);
	float height = (float)(rt.bottom - rt.top);

	LPDIRECT3DSURFACE9 pSurface = nullptr;
	CAStarGame::GetDevice()->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pSurface);
	D3DSURFACE_DESC desc = {
   };
	pSurface->GetDesc(&desc);

	return D3DXVECTOR2(point.x * desc.Width/width, point.y * desc.Height/height);
}

void CChildView::OnPaint() 
{
   
	CPaintDC dc(this); // 用于绘制的设备上下文
	
	// TODO: 在此处添加消息处理程序代码
	m_pGame->Render();
	
	// 不要为绘制消息而调用 CWnd::OnPaint()
}



int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
   
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  在此添加您专用的创建代码
	SetTimer(1, 10, nullptr);

	return 0;
}


void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
   
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	m_pGame->OnLButtonDown(GetViewportPoint(point));
	CWnd::OnLButtonDown(nFlags, point);
}


void CChildView::OnRButtonDown(UINT nFlags, CPoint point)
{
   
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	m_pGame->OnRButtonDown(GetViewportPoint(point));
	CWnd::OnRButtonDown(nFlags, point);
}


void CChildView::OnTimer(UINT_PTR nIDEvent)
{
   
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	switch (nIDEvent)
	{
   
	case 1:
		m_pGame->Render();
		break;
	default:
		break;
	}
	CWnd::OnTimer(nIDEvent);
}


void CChildView::OnSize(UINT nType, int cx, int cy)
{
   
	CWnd::OnSize(nType, cx, cy);
	// TODO: 在此处添加消息处理程序代码
	if (cx > 0 && cy > 0)
	{
   
		if (!m_pGame->GameInited())
		{
   
			m_pGame->Init(m_hWnd);
		}
	}
}


void CChildView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
   
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	m_pGame->OnKeyDown(nChar, nRepCnt, nFlags);
	CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}

主要监听鼠标左右键和键盘数字1事件,其他的倒是没有什么。

CAStarGame介绍

游戏管理摄像机、视口和游戏物体管理器以及Direct3D对象。至于为什么要用Direct3D,就是纯粹为了练习。

#pragma once

class GameObjectManager;
class CAStarCamera;
class CAStarViewport;

class CAStarGame
{
   
public:
	CAStarGame();
	virtual ~CAStarGame();

public:
	static CAStarGame* GetInstance();
	static LPDIRECT3DDEVICE9 GetDevice();
	static CAStarCamera* GetCamera();
	static CAStarViewport* GetViewport();

protected:
	static CAStarGame* m_sInstance;

public:
	bool Init(HWND hWnd);
	void Render();
	void Release();
	void OnLButtonDown(const D3DXVECTOR2& point);
	void OnRButtonDown(const D3DXVECTOR2& point);
	void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);

public:
	const bool GameInited() {
    return m_bInit; }

protected:
	LPDIRECT3D9 m_pD3D;
	LPDIRECT3DDEVICE9 m_pDevice;

	GameObjectManager* m_pGameObjectManager;
	CAStarCamera* m_pCamera;
	CAStarViewport* m_pViewport;

protected:
	bool m_bInit;
};


游戏初始化

创建d3d对象和根据窗口句柄创建设备,根据客户区大小创建正交摄像机和视口,是正交摄像机。

bool CAStarGame::Init(HWND hWnd)
{
   
	m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
	if (!m_pD3D)
		return false;

	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS));
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
	d3dpp.BackBufferCount = 1;
	d3dpp.EnableAutoDepthStencil = TRUE;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	if (FAILED(m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &m_pDevice)))
		return false;

	m_pDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
	m_pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
	m_pDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

	if (!m_pGameObjectManager->Init())
		return false;

	if (!m_pCamera->Init())
		return false;

	RECT rt;
	GetClientRect(hWnd, &rt);

	DWORD width = rt.right - rt.left;
	DWORD height = rt.bottom - rt.top;
	float radio = width * 1.0f / height;
	m_pCamera->SetProjMatrixOrth(10.0f * radio, 10.0f, 0.1f, 100.0f);
	m_pCamera->SetViewMatrix(D3DXVECTOR3(0.0f, 0.0f, -10.0f), D3DXVECTOR3(0.0f, 0.0f, 0.0f), D3DXVECTOR3(0.0f, 1.0f, 0.0f));
	m_pCamera->Active();

	if (!m_pViewport->Init())
		return false;

	m_pViewport->SetParam(0,0, width , height, 0.0f, 1.0f);
	m_pViewport->Active();

	m_bInit = true;

	return true;
}

游戏逻辑继续向游戏对象传

主要是键盘和鼠标事件。


void CAStarGame::OnLButtonDown(const D3DXVECTOR2& point)
{
   
	D3DXVECTOR3 vecIn(point.x, point.y, 1.0f);
	D3DXVECTOR3 vecOut;

	m_pViewport->InvTransform(vecIn, vecOut);
	m_pGameObjectManager->OnLButtonDown(vecOut);
}

void CAStarGame::OnRButtonDown(const D3DXVECTOR2& point)
{
   
	D3DXVECTOR3 vecIn(point.x, point.y, 1.0f);
	D3DXVECTOR3 vecOut;

	m_pViewport->InvTransform(vecIn, vecOut);
	m_pGameObjectManager->OnRButtonDown(vecOut);
}

void CAStarGame::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
   
	m_pGameObjectManager->OnKeyDown(nChar, nRepCnt, nFlags);
}

视口InvTransform

鼠标点击转换到游戏世界很关键。两种办法,注释是一种,直接调用DX的API是另外一种

void CAStarViewport::InvTransform(const D3DXVECTOR3& vecIn, D3DXVECTOR3& vecOut)
{
   
	/*D3DXMATRIX matViewportScale;
	D3DXMatrixIdentity(&matViewportScale);
	matViewportScale._11 = 1.0f * m_Viewport.Width / 2.0f;
	matViewportScale._22 = -1.0f * m_Viewport.Height / 2.0f;
	matViewportScale._33 = m_Viewport.MaxZ - m_Viewport.MinZ;
	matViewportScale._41 = m_Viewport.X * 1.0f + m_Viewport.Width / 2.0f;
	matViewportScale._42 = m_Viewport.Y * 1.0f + m_Viewport.Height / 2.0f;
	matViewportScale._43 = m_Viewport.MinZ;
	D3DXMATRIX invViewportScale;
	D3DXMatrixInverse(&invViewportScale, NULL, &matViewportScale);
	D3DXVECTOR3 vecPort;
	D3DXVec3TransformCoord(&vecPort, &vecIn, &invViewportScale);

	D3DXMATRIX matProj = CAStarGame::GetCamera()->GetProjMatrix();
	D3DXMATRIX invMatProj;
	D3DXMatrixInverse(&invMatProj, NULL, &matProj);
	D3DXVECTOR3 vecProj;
	D3DXVec3TransformCoord(&vecProj, &vecPort, &invMatProj);
	D3DXMATRIX matView = CAStarGame::GetCamera()->GetViewMatrix();
	D3DXMATRIX invMatView;
	D3DXMatrixInverse(&invMatView, NULL, &matView);
	D3DXVec3TransformCoord(&vecOut, &vecProj, &invMatView);*/

	D3DXMATRIX matProj = CAStarGame::GetCamera()->GetProjMatrix();
	D3DXMATRIX matView = CAStarGame::GetCamera()->GetViewMatrix();
	D3DXVec3Unproject(&vecOut, &vecIn, &m_Viewport,
		&matProj, 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

当当小螳螂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值