一个非常简单的方法,它会给你一些类似于 Wolfenstein 3D 的结果(但使用 OpenGL 的优点要容易得多 :-P)是让世界由一个 2D 网格组成,例如:
+-+-+-+-+-+-+-+
| | | | | | | |
+-+-+-+-+-+-+-+
| | | | | | | |
+-+-+-+-+-+-+-+
| | | | | | | |
+-+-+-+-+-+-+-+
| | | | | | | |
+-+-+-+-+-+-+-+
每个单元格都应包含有关单元格的信息,例如单元格是空的还是实心的,如果是实心的,它有什么材料,或者如果它是空的,地板和天花板有什么材料。如果它为空,您还可以添加其他信息,例如灯光投射、对某些实体的引用(用于添加游戏模型或其他东西)、逻辑标志(例如“退出”或“开始”或“危险”的标志)等. 一个简单的结构类似于(在 C 中):
#define SOLID 0x0001
#define START 0x0002
#define EXIT 0x0004
#define HAZARD 0x0008
struct _cell_t
{
unsigned int flags;
GLuint texture;
entity_t* entity;
} cell_t;
在上述结构中,flags 是单元格的标志。如果设置了SOLID 标志,则单元格是实心(墙),如果设置了START 标志,则单元格是玩家的开始位置,如果设置了EXIT 标志,则单元格是迷宫的出口位置,并且最后,如果设置了HAZARD,则单元格会对播放器造成伤害。当然,您可以在那里添加更多标志(例如投射光的LIGHT 标志、将单元格标记为秘密区域的一部分的SECRET 标志等),但您明白了:-)。
texture 字段是一个 OpenGL ES 纹理名称(使用glGenTextures 生成并使用glTexImage2D 填充)。这用于渲染迷宫(为了简单起见,这里我只使用一个纹理,但如果你想显示更有趣的世界,你应该为实体单元指定四个纹理,为空单元指定两个纹理 - 这四个纹理将是每一侧的纹理一个单元格/墙壁,两个纹理将是单元格的地板和天花板)。
entity 字段只是对实体的引用,如果您想添加实体。静态实体(如花瓶、灯、桌子等)通常由网格和材质组成。当然,如果您想添加动态实体(例如怪物、拾取物品等),您需要将动画信息添加到实体结构中,并将“动态”实体列表与单元格分开,因为这些实体会移动环游世界,因此不会“属于”一个单元格(出于优化原因,您可能希望在每个单元格中分配一个“临时”实体,但优化是您应该在之后让引擎/游戏运行)。
现在要渲染迷宫,只需渲染所有单元格(稍后您可能想要进行一些泛洪填充或光线投射测试以仅渲染可见的内容,但现在再次不必担心)。对于每个单元格,检查单元格的标志是否包含SOLID 标志,如果是,则渲染组成单元格的四个四边形(想象一个没有顶部和底部的立方体)。如果单元格没有SOLID 标志,则渲染单元格的地板和天花板的两个四边形(想象同一个立方体,但现在只有一个顶部和一个底部)。如果您正在渲染的单元格有一个不为空的entity 字段,那么也渲染该实体。
如果你这样做,你就会有一个迷宫:-)。一些优化提示:
- 您会很快意识到您正在渲染许多永远不会看到的四边形。如果这确实对速度有影响(并且在您尝试仅渲染可见单元格之后),您可能希望扩展包含单元格壁实际存在的标志的结构。例如,想象这三个连续的单元格:
ABC。单元格A 和C 有三面墙(A 的左、上、下和B 的右、上、下),B 的单元格有两面墙(上和下)。要标记墙壁是否存在,请检查每个单元格周围的单元格。
- 如果一个单元格上方有一个
SOLID 单元格,则它的“向上”墙不存在。其左侧单元格、右侧单元格和下方单元格也是如此。
- 如果单元格位于地图边缘,则始终缺少一堵墙。如果在拐角处,总是缺少两堵墙。
- 在初始化游戏时进行此计算,而不是在渲染时!
-
在确定哪些单元格可见后进行这些测试。在开始为此编写代码之前,请保留一份备份(希望您会使用一些版本控制系统,例如 Subversion,但即使不只是将代码复制到某处)并比较两个版本的结果。不移除空墙而是使用 VBO 对所有墙壁使用相同的网格实际上可能更快 - 特别是在 iPhone 3GS 上。
- 如果您添加动态实体并拥有我上面提到的“临时实体”,您实际上可以自动丢弃不可见的动态实体,一旦您有一种方法来确定哪些单元格是可见的,哪些不可见:如果一个单元格不可见,它的关联的动态实体也将不可见(但请注意:一个实体可以触摸多个单元格)。
- 一种非常简单但非常粗略的不渲染所有单元格的方法,就是只渲染相机周围的单元格,例如:
.
+-+-+-+-+-+-+-+
| |X|X|X| | | |
+-+-+-+-+-+-+-+
| |X|C|X| | | |
+-+-+-+-+-+-+-+
| |X|X|X| | | |
+-+-+-+-+-+-+-+
| | | | | | | |
+-+-+-+-+-+-+-+
(这里 C 是摄像头所在的单元格,X 是摄像头周围的单元格 - 当然,出于说明目的,我只使用了与摄像头相隔一个单元格的单元格,但您应该使用更多的单元格 - 比如 16以单元格为例。只要确保您的地图永远不会有一个可以看到超过 16 个单元格或任何您决定的空白区域)
当然,投射光线可能会产生更好的效果:-)。
嗯,我认为这些信息足以为您提供一个起点:-)。当然,Stackoverflow 在这里可以解决您可能遇到的任何其他问题:-P