`

一个基于组合模式的游戏地图系统

 
阅读更多

from: http://blog.csdn.net/popy007/article/details/8301133

 

一个基于组合模式的游戏地图系统

 

地图系统

 

地图系统是游戏开发永恒不变的一个主题。在大多数游戏开发中,我们都需要和地图打交道。不同的游戏可能采用不同的地图系统,比如对于2D游戏来说,可能的地图类型包括:

 

1)Rectangular tile,即矩形tile。

2)Isometric,即等测地图。

3)Hexagonal tile,即六边形tile。

 

等等。

 



 

而对于3D游戏来说,一般包括两种主要类型:

 

1)基于3D空间的2D地图

2)完全3D空间地图

 

对于第一种类型,实际上是2D地图系统的一种3D推广,游戏中的地图还是在一个2D平面上,但是在3D空间的一个平面上。场景中的游戏对象可以都是3D模型。但地图本身仍然通过2D地图的方式进行处理。

 

第二种一般在3D图形学中叫做场景管理(Scene Management)。这是对真3D场景的一种处理方式:地图信息不再处于某个平面上,而是具有任意的3D几何信息。对于处理这种非规则的3D几何地图系统,我们一般采用分类的方式进行单独处理,比如:

 

1)室外地形系统

2)室内地图系统

3)基于portal的系统

 

等等。针对不同的类型,3D场景系统一般采用不同的管理方法,比如quadtree、octree、bsp、portal等等场景管理方式。可以说这个领域很广泛、也很复杂。

 

我们当然不可能具体地讨论所有这些地图系统。那么我们讨论什么呢?我们将讨论一种思想,这种思想贯穿于所有的这些地图管理方法之中,以提供一种健壮、合理、便利的代码结构。而这种结构不论是在游戏产品还是在开发工具中,都扮演着举足轻重的作用。这就是层次体结构。

 

 

层次体结构

 

对于复杂的问题以及结构,我们都倾向于一种称为“分治法”的方式进行处理。层次体结构实际上就是一种分治法,它通过一种树形结构来表示“整体-部分”的层次概念。基于这种方法,开发者可以很容易地对系统进行不同等级的灵活控制,同时获得一种复用性优势。

 

我们来举一个简单例子,来让大家更好地理解这些抽象概念。对于最简单的2D矩形tile引擎来说,开发者们经常会把场景分成多个层(layer)和一个根(root),如下图所示:

 



这就是一个最简单的层次体结构。它表明了一种整体和局部的从属关系:root可以看作这个地图本身,它代表整体。而它的子结点,则看作是这个地图的每个层,他们代表局部,属于整体。我们再具体一点,假设我们把地图分成了3层:


Layer 1 - 地面层

Layer 2 - 中间层

Layer 3 - 天空层


地 面层是永远处于”最下面“的层,地图上的任何东西都会处于它的上面,这一层包括普通土地、草坪、沙土地、公路等类型的tile。中间层就是地图上的对象 层,它永远处于地面层之上,这一层包括:房子、树木、玩家角色、NPC等活动对象,这些对象之间的位置关系会随时变化,因此互相遮挡的关系也会变化,需要 实时进行排序。天空层包括天上飞的鸟、飘浮的云朵等对象,它们永远处于地面层之上。


更进一步地,我们可以在每个layer上再增加子layer——这可以根据具体游戏对象的类型,比如把所有沙土地对象,都放到一个layer上,当玩家走到上面的时候,会减慢速度。如下图所示:

 



如我们所看到的,对这些对象进行3个layer分类,我们获得了很多好处:

 

1)分区渲染-渲染的顺序可以通过layer自然的处理:地面、中间、天空,这样的顺序保证了渲染结果的可视关系的正确。

2)分区更新-只有中间层的对象需要实时排序,这避免了对无需排序对象进行排序的无效计算。另外,可以直接用沙土层和玩家做碰撞检测——避免了不必要的计算。

3)总体控制-在对地图进行编辑的时候,我们可以非常方便地显示、隐藏、移动、删除不同的层。被操作的每个层上的对象都得到统一的处理。

 

而 这些好处也不仅仅局限于矩形2D tile地图,任何地图系统都可以从中得到这样的好处。比如在3D的场景管理中,quadtree、octree、bsp本身就是一种层次体结构。它们通 过树形结构对场景进行划分,从而以一定的规则将场景中的多边形安排到树中。另外,对于3D的所谓保留模式(Retained Mode)渲染,实际上就是通过把场景安排成一颗场景树来进行处理。系统一般会提供一个类似这样的接口:

 

void GraphicsEngine::render( SceneNode& sceneTreeRoot );

 

sceneTreeRoot就是场景树的根结点,从该结点向下就可以遍历整个树形结构,并进行任何你希望的操作——这当然以渲染为主。

 

在一些支持保留模式渲染的引擎中,一般设计师或美工会通过一些所见即所得的工具进行场景编辑,放置模型,虚拟物体,架设相机等等。最后导出一个文件,供引擎使用。而导出的往往就是一颗场景树,连相机都会作为一个结点存在于该结构中。

 

复用性

 

层 次体结构的另一个巨大优势在于复用性,而这往往体现在设计和实现两个层面上。想象我们在地图里面将几块石头摆放成了一种特殊形状,这种形状暗示了某种游戏 中的宗教意义。而根据设计需求,该形状可能会摆在地图中的很多位置。由于整个地图通过组合模式被建立成了一棵场景树,我们则可以把这个形状的几个石头,单 独的做成一个子layer。然后在不同的地方(结点)放置这些子layer。而在代码级别上,这些layer中的石头实际上是通过类对象指针保存的,因此 存放开销完全可以接受。所以,我们每次给一个结点增加一个这样的layer,就添加了这些石头所组成的一个形状——我们实际上是通过一种组合的方式进行特 定结构的复用。请注意,这些复用是可以嵌套的!这意味着你可以用这些子layer再组合更复杂的层,然后服用这些更复杂的layer。这种复用带来的威力 是巨大的,在两方面提供优势:

 

1)设计开销 -通过定义一个子layer,我们可以反复使用该layer,还可以嵌套!

2)内存开销 -只通过指针存储对象,实际对象内存只有一份。

 



这种复用性带来的利益驱使我们开发了一套2D动画系统——用场景树的方式组织动画。允许对不同复杂组件(可以看作这里的layer)的嵌套。以后如果条件允许,我会写一篇关于该系统的文章,帮助一下初学游戏开发的朋友理解2D动画系统。

 

 

 

层次体的简单实现

 

层次体结构实际上是一种组合(Composite)模式。根据GoF的著作《设计模式》所描述:组合模式通过一种树形结构来体现一种“整体和部分”的观念,从而让处理单一对象和对象聚合存在一种统一的形式。下图所示即组合模式:

 



Leaf 是单一对象,composite是组合对象,也就是一个对象聚合体。它们都继承自component类,拥有相同的操作方式Operation——这是关 键,也是组合模式的核心思想:在客户(client)看来,操作一个leaf和一个composite没什么区别,都调用它的Operation,但 composite是个聚合体,它会在自己的Operation中调用每一个聚合对象的Operation。

 

这里单一对象就可以理解为地图中的一个具体对象(树木、房子、NPC等等),而组合对象则可以看成是地图的一个layer——它聚合其它对象(树木、房子、NPC,或者是另一个layer)。

 

下面我们实现一个简单的层次体结构,从代码级别来深入认识该系统:

 

class MapNode
{
public:
	virtual ~MapNode() {}

	virtual void cycle() = 0;
	virtual void draw( GraphicsContext& g ) = 0;
};

class Node_Layer : public MapNode
{
public:

	void addChild( SceneNode* child )
	{
		m_children.push_back( child );
	}

	void removeChild( SceneNode* child )
	{
		for( std::vector< SceneNode* >::iterator it = m_children.begin();
			it != m_children.end(); ++it )
		{
			if( *it == child )
			{
				m_children.erase( it );
				break;
			}
		}
	}

	virtual void cycle()
	{
		for( int i = 0; i < m_children.size(); ++i )
		{
			m_children[i]->cycle();
		}
	}

	virtual void draw( GraphicsContext& g )
	{
		for( int i = 0; i < m_children.size(); ++i )
		{
			m_children[i]->draw( g );
		}
	}


protected:
	std::vector< SceneNode* > m_children;
};

class Node_Tree : public MapNode
{
public:

	virtual void cycle()
	{
		Update the state of the tree...
	}

	virtual void draw( GraphicsContext& g )
	{
		Draw the tree via g...
	}
};

 

 

以上是一个非常简单的组合代码结构。 MapNode是地图结点基类,我们用它的指针或引用来操作整个系统。它提供两个抽象方法

 

void MapNode::cycle

void MapNode::draw

 

分 别用来进行帧更新和渲染,具体实现由子类决定。Node_Layer就是地图层类,它就是所谓的聚合体。Node_Tree是一个单一对象,它表示地图上 的树这种对象。可以看到它们对具体操作的实现清晰地表达了整体-部分的思路:Node_Layer把操作分散到各个子对象中,而Node_Tree则是老 老实实作自己的事情。这便是一个最简单的层次体结构实现。一个简单的游戏和可能会如此使用上述代码:

 

class Node_Root : public Node_Layer {};

class Node_Player : public MapNode
{
public:

	virtual void cycle()
	{
		Update the state of the player...
	}

	virtual void draw( GraphicsContext& g )
	{
		Draw the player via g...
	}
};

Node_Root root;
Node_Player player;
Node_Tree tree;

root.addChild( &player );
root.addChild( &tree );

for(;;)
{
	root.cycle();
	root.draw( g );
}

 

 

我们建立了一个root类作为世界的根结点。然后生成了一个玩家和一棵树,都放进了场景里,接着进入了我们的游戏循环。还有比这更简单的么?

 

以该结构作为一个开始,开发者可以进行扩展。包括加入MapNode的共性成员:

 

位置

可见性标志

 

等等。对于非聚合类型的MapNode子类的设计,那就和你的游戏相关了。比如对于Node_Player来说,可以增加移动控制、生命、动画等等。这里提供的只是一种设计上的思路,而经过开发者的细节设计和扩展,该系统将发挥实际功效。

 

 

总结

 

上 面我们介绍了层次体概念以及该结构的简单实现。它是一种优秀的地图系统、场景管理方法。不论是在2D还是3D中,都可以发挥巨大的威力。不论对于游戏开发 初学者还是专家,在实际的游戏代码中你都会看到它的影子。因此,独立地考察和分析该系统对于开发者来说都是有很必要的。

 

 

 

 

 

 

 

  • 大小: 86.8 KB
  • 大小: 25.7 KB
  • 大小: 56.1 KB
  • 大小: 123.7 KB
  • 大小: 8.2 KB
分享到:
评论

相关推荐

    游戏设计模式简介

    9. **组合模式**:在构建复杂的游戏世界时,组合模式能帮助我们处理对象之间的层次结构,如游戏地图中的区域、房间和物品,它们可以通过组合模式形成一个整体。 10. **享元模式**:对于大量相似但有微小差别的对象...

    基于结构特征的组合线划类地图符号识别

    由于这类符号的结构复杂,且种类繁多,因此,其自动识别和提取是地图模式识别领域中的一个难点。 为了实现有效的地图符号识别,本文作者魏钜杰提出了基于线划结构特征的提取和识别算法。这项研究的目的是将传统的...

    基于Spark的车联网分布式组合深度学习入侵检测方法.pdf

    综合以上内容,本文提出了一个在现代车联网环境下,通过利用分布式系统架构、深度学习技术和大数据处理能力,实现高效准确入侵检测的全新方法。这不仅为车联网安全提供了新的技术路径,也为其他领域的安全问题提供了...

    基于webgis的简单系统

    SSH框架是Java Web开发中的一个经典组合,用于构建高效、可扩展的企业级应用。Struts2作为MVC模式的实现,负责处理用户的请求并控制业务流程;Spring则提供了依赖注入和面向切面编程的能力,增强了代码的灵活性和可...

    基于Python的离线Google地图操作实现

    同时,本文也提供了一种基于Python的离线Google地图操作实现方法,可以为GIS系统开发提供便利。 关键词:Google地图;离线;地图瓦片;地图操作 一、引言 互联网技术和地图测绘技术的发展推动了一系列与地图相关...

    基于MapInfo的专题地图制作

    6. **饼图地图**:在地图上的每个位置放置一个饼图,用于表示多个属性之间的比例关系,如不同行业的就业人数比例。 #### 四、总结 基于MapInfo的专题地图制作,不仅是一种数据可视化的手段,更是地理信息分析的...

    【纯JAVA语言做RPG游戏】1.做个瓷砖地图生成器

    在本项目中,我们将探讨如何使用纯Java语言开发一个RPG(角色扮演游戏)中的瓷砖地图生成器。这个生成器能够自动生成游戏世界的基础布局,为玩家提供丰富的探索环境。地图生成是游戏开发的重要组成部分,特别是在2D...

    JSP基于SSM校园地图导航系统可升级SpringBoot毕业源码案例设计.zip

    这是一个基于JSP、SSM(Spring、SpringMVC、MyBatis)框架的校园地图导航系统,可升级至SpringBoot的毕业设计案例。这个项目旨在为校园内的用户提供精确的导航服务,帮助他们快速找到目的地。下面我们将深入探讨该...

    java实验策略规划模式、观察者模式和组合模式.docx

    文档中提到了使用组合模式来构建一个游戏地图。游戏中可能存在多个不同类型的图元,如墙、道路等,通过组合模式可以方便地管理这些元素。 **实验实例**: - **抽象类/接口**:定义一个 `MapElement` 接口或抽象类,...

    基于SSM+百度地图API接口+layui前端框架+mysql的防疫打卡系统项目源码.zip

    这是一个基于Java SSM(Spring、SpringMVC、MyBatis)框架、百度地图API、layui前端框架和MySQL数据库构建的防疫打卡系统项目源码。这个系统的主要目的是在疫情期间,为组织或机构提供一种便捷的方式,让员工或者...

    基于AJAX技术的WebGIS客户端地图查询.pdf

    这种方式可以摒弃了以往地图和数据查询都在一次请求中完成的方式,实现了功能模块的异步模式通信,每个功能模块都可以独立完成与服务器端的通信。 此外,该文还讨论了基于AJAX技术的WebGIS客户端地图查询系统的设计...

    基于Linux平台下地理信息系统软件开发.pdf

    Kylix是第一个专为Linux平台设计的完整、高性能的快速应用程序开发(RAD)工具,基于组件的编程模式使开发者可以快速构建GIS软件。它提供了丰富的组件库,简化了开发过程,并支持面向对象编程,有利于提高软件的可...

    吉林大学软件设计模式大富翁游戏(C++实现)

    5. **组合模式**:在处理游戏地图和地产所有权时,可能会用到组合模式,将单一的地产和地产组合视作同一类型,简化了操作和遍历。 6. **装饰者模式**:可能用于增强或修改游戏对象的行为,比如给玩家添加特殊能力或...

    基于ComGIS的城市公交查询系统_李天文

    组件式地理信息系统(Component GIS, ComGIS)是一种新型的GIS开发平台,它通过将GIS功能分解成多个独立的组件,使得开发者可以根据实际需求灵活地选择和组合这些组件来构建应用程序。ComGIS的优势在于: - **高效...

    基于google地图的Android系统导航应用设计.docx

    ### 基于Google地图的Android系统导航应用设计 #### 一、引言 随着智能手机技术的迅速发展,移动互联网的应用越来越广泛。特别是在导航领域,智能手机已成为人们的贴身向导。本篇文档介绍了一款基于Google Maps ...

    软件设计模式实验2结构型模式源码

    组合模式:用组合模式显示其所选商品信息,并计算所选商品总价的功能。说明:假如王同学到万达生活用品店购物。用 1 个红色小袋子装了 2 包衡阳特产(单价 25 元)、1 张衡阳地图(单价5.8元).....5.享元模式:在...

    建筑群组合直线模式识别的模板匹配方法.docx

    总结来说,本文提出的模板匹配方法为建筑群组合直线模式的识别提供了一个有效的工具,它融合了地理学、认知科学和计算机科学的理论,有望在城市规划、地理信息系统和地图制作等领域得到广泛应用。这种方法不仅有助于...

    网络游戏-利用神经网络算法补偿和修正的GPS+INS组合导航方法及系统.zip

    GPS是一种基于卫星信号的全球定位系统,可以提供全球范围内的三维位置、速度和时间信息。然而,由于信号干扰、多路径效应和卫星遮挡等因素,GPS信号可能存在误差,导致定位不准确。另一方面,INS利用加速度计和陀螺...

Global site tag (gtag.js) - Google Analytics