从零开始重写KOK1(万王之王1) —— (2)优化地图加载

本来想在第2篇说明物体遮挡与寻路的开发过程,但是因为我把这问题想简单了,现在已经完成了遮挡与寻路,但是中间的过程非常多,第一篇文章的系统结构需要做一些修改才可以,这里先说一下地图加载的相关问题。

 

首先是效果图:

从零开始重写KOK1(万王之王1) —— (2)优化地图加载_第1张图片

 

可直接运行版本下载:>>点击进入下载页<<

 

在第一篇文章《从零开始重写KOK1(万王之王1) —— (1)让人物可在地图上使用鼠标跑动》中,地图是这样的过程加载的:

1. 在Map对象初始化时将每个小图个加载到一个整体的大地图中,如图:

从零开始重写KOK1(万王之王1) —— (2)优化地图加载_第2张图片

2. 在玩家位置作出修改后,也修改屏幕矩形左上角的位置,因为玩家是在中间,所以要维持屏幕的x,玩家的x - 1/2屏幕宽,纵向同理。

以下是Player类的Move()中的代码,用来调整屏幕矩形的位置:

g_GameScreen.MapX = MapX - g_GameScreen.ScreenWidth / 2; g_GameScreen.MapY = MapY - g_GameScreen.ScreenHeight / 2;

3. 要限制住玩家的位置,不能够使得屏幕矩形超出世界地图。在老的地图管理器是这样做的,限制玩家的目的坐标,不可以小于屏幕宽的一半,不可以大于世界地图宽度 - 屏幕宽的一半,纵向同理。这样就不会出现屏幕画地图外的情况了。但这样做就使得实际地图的内容小于了现在世界地图的大小,因为要在世界地图四周补上一圈黑色的贴图,这样玩家才知道到达了地图的边界。文字上可能不好理解,请看下图:

从零开始重写KOK1(万王之王1) —— (2)优化地图加载_第3张图片

这个图假定玩家走到了地图的右下角,因为屏幕矩形(蓝色框)不能超过世界地图,所以要在世界地图里面加这么一圈灰色的作为边界的纹理,所以也就说,实际的地图就比这个地图Data存放的要小了,这显然不是我们想要的。而且最重要的问题是,当地图非常之大时,这个超大的地图表面将占用巨大的显存或内存。

 

那么我们想要的地图管理器的目标就是:

——整个地图Data就是实际的地图,不包括边界纹理,当因为玩家的移动而导致屏幕矩形超出了地图范围时,自动补黑色。

 

要实现这种效果可以想到这种方法:地图表面大小= 屏幕大小,每一帧重画应当出现的地图块。但问题也随之而来,我们要在每1帧都去画一遍视窗内的地图,效率不高。那不如我们每次画一个区域的地图,当屏幕在这个区域内就不重画,而是裁剪适当的位置,当超出这个区域时,重新生成新区域,并且剪裁适当的地图,如下图:

从零开始重写KOK1(万王之王1) —— (2)优化地图加载_第4张图片

总结一下:

1. 首先确定屏幕矩形在当前地图表面内,在则不重画,直接剪裁适当位置,如果不在,进入2。

2. 重新绘制地图表面,根据屏幕矩形,计算地图表面中的每个小块:

    2.1 如果不在世界地图中,不处理,因为默认就是黑色。

    2.2 如果在,则计算是哪块世界地图,将适当的地图块话在这里。

3. 重新绘制后,剪裁适当的位置,画在离屏表面上。

 

下面贴出代码:

Map.h

#pragma once #include <ddraw.h> #include "GameScreen.h" class Map { public: int Init(); int Draw(); void Release(); int TileWidth; // 每个地图纹理的宽度 int TileHeight; // 每个地图纹理的高度 LPDIRECTDRAWSURFACE7 *TileSurfaces; // 存放所有地图纹理的表面的数组 int SurfaceCount; // 存放所有地图纹理的表面的数组长度 int *Data; // 存放地图索引的数组,即哪个格放哪个纹理 int MapWidth; // 地图横向有多少块 int MapHeight; // 地图纵向有多少块 LPDIRECTDRAWSURFACE7 MapViewSurface; // 容纳可视区域的最小所有完整Tile的地图表面 LPDIRECTDRAWCLIPPER MapViewClipper; int MapViewWidth; // 地图包含可视区域的最小块数(X) int MapViewHeight; // 地图包含可视区域的最小块数(Y) int MapViewX; // 当前地图表面的左上角的块索引(X) int MapViewY; // 当前地图表面的左上角的块索引(Y) };

 

Map.cpp

#include "StdAfx.h" #include "VideoManager.h" #include "GameScreen.h" #include "Player.h" #include "Map.h" int Map::Init() { MapWidth = 20; MapHeight = 20; TileWidth = 80; TileHeight = 40; SurfaceCount = 1; TileSurfaces = new LPDIRECTDRAWSURFACE7[SurfaceCount]; Data = new int[MapWidth * MapHeight]; Bitmap24 floor; floor.Load(TEXT("pic//map//floor.bmp")); TileSurfaces[0] = g_GameScreen.Video.CreateSurface(TileWidth, TileHeight, 0); g_GameScreen.Video.PickBitmap(TileSurfaces[0], &floor, TileWidth, TileHeight, 0, 0); floor.Release(); for (int iy = 0; iy < MapHeight; ++iy) { for (int ix = 0; ix < MapWidth; ++ix) { Data[ix + iy * MapWidth] = 0; } } MapViewWidth = g_GameScreen.ScreenWidth / TileWidth + 1; MapViewHeight = g_GameScreen.ScreenHeight / TileHeight + 1; MapViewSurface = g_GameScreen.Video.CreateSurface(MapViewWidth * TileWidth, MapViewHeight * TileHeight, 0); RECT clip_rect = { 0, 0, MapViewWidth * TileWidth, MapViewHeight * TileHeight }; MapViewClipper = g_GameScreen.Video.DDraw_Attach_Clipper(MapViewSurface, 1 , &clip_rect); MapViewX = -1; MapViewY = -1; return 1; } int Map::Draw() { // 判断是否超出了上次画的MapViewSurface int newViewX = g_GameScreen.MapX / TileWidth + (g_GameScreen.MapX >= 0 ? 0 : -1); int newViewY = g_GameScreen.MapY / TileHeight + (g_GameScreen.MapY >= 0 ? 0 : -1); if (MapViewX != newViewX || MapViewY != newViewY) { // 清表面 g_GameScreen.Video.FillSurface(MapViewSurface, 0); MapViewX = newViewX; MapViewY = newViewY; // 重新画MapViewSurface for (int y = 0; y < MapViewHeight; ++y) { for (int x = 0; x < MapViewWidth; ++x) { RECT dest_rect = { x * TileWidth, y * TileHeight, (x+1) * TileWidth, (y+1) * TileHeight }; int srcX = MapViewX + x; int srcY = MapViewY + y; if (srcX >= 0 && srcY >= 0 && srcX < MapWidth && srcY < MapHeight ) { MapViewSurface->Blt(&dest_rect, TileSurfaces[Data[MapWidth*(srcY) + srcX]], NULL, DDBLT_WAIT, NULL); } } } } // 计算Screen对MapViewSurface的偏移量,并Blt到离屏表面 RECT rect; if (g_GameScreen.MapX >= 0) rect.left = g_GameScreen.MapX % TileWidth; else rect.left = TileWidth - abs(g_GameScreen.MapX) % TileWidth; if (g_GameScreen.MapY >= 0) rect.top = g_GameScreen.MapY % TileHeight; else rect.top = TileHeight - abs(g_GameScreen.MapY) % TileHeight; rect.right = rect.left + g_GameScreen.ScreenWidth; rect.bottom = rect.top + g_GameScreen.ScreenHeight; g_GameScreen.Video.Lpddsoffscreen->Blt(NULL, MapViewSurface, &rect, DDBLT_WAIT, NULL); return 1; } void Map::Release() { for (int i = 0 ; i < SurfaceCount; ++i) { TileSurfaces[i]->Release(); TileSurfaces[i] = NULL; } MapViewClipper->Release(); MapViewSurface->Release(); MapViewSurface = NULL; delete [] TileSurfaces; delete [] Data; }

 

下面给出整个项目源码,因为最近的研究是一系列的,所以这个版本的源码已经有了遮挡、寻路等功能,但是后面的功能不影响Map的代码。

下载地址:>>点击进入下载页<< 资源分是1,有兴趣的朋友下吧:)

你可能感兴趣的:(优化,null,Class)