上回说到我们已经可以控制小方块的移动了,现在我们要把这个小方块放置到游戏场景里

剥去美术的外壳,游戏场景其实就是一个标记了玩家可行走与不可行走的空间

对于2D游戏,它是一个平面网格,3D游戏就是一个3维空间网格

我们先来看2D的情况:

首先我们用一个二维数组记录地图上所有像素坐标点(整数坐标点)的可行走与不可行走的信息

grid_map = [[0 for i in range(M)] for i in range(N)]

地图中的阻挡就是一块连续的不可行走区域

以最简单的矩形阻挡为例,我们定义一个类来描述一个阻挡:

class Block(object):

def __init__(self,x,y,w,h):

self.rect = Rect(x,y,w,h)

for i in range(y,y+h):

for j in range(x,x+w):

grid_map[i][j]=1

在构造函数中把二维数组中对应的元素标记为不可行走(0可行走,1不可行走)

这样我们每构造一个Block类的对象,就在地图上生成了一个阻挡

我们在地图初始化中创建所有的阻挡

def InitMap():

for i in range(N):

for j in range(M):

grid_map[i][j]=0#初始化地表,都是可行走

for i in range(0,M,50):#创建若干阻挡

b1 = Block(i,0,10,100)

b2 = Block(i,200,10,100)

b3 = Block(i,400,10,100)

block_list.append(b1)

block_list.append(b2)

block_list.append(b3)

如果绘制出来,将得到这样的一张地图,黑色的是可行走区域,灰色的表示阻挡块

二维地图.png

接下来我们再让这些阻挡能够真的影响玩家的移动

假设玩家出生时不会出生在阻挡里

那么我们只需要在表示玩家的矩形发生移动时,判断矩形边界上的点是否都在可行走区域即可

onblock = 0

#分别判断两条横边和两条竖边,tx,ty是矩形起始坐标点,20是默认边长

for i in range(tx,tx+20):

if(grid_map[ty][i] or grid_map[ty+19][i]):

onblock = 1

break

for i in range(ty,ty+20):

if(grid_map[i][tx] or grid_map[i][tx+19]):

onblock = 1

break

完整代码可以从这里获取

下面我们将把地图推广到3D空间

首先来感受一下游戏中会用到的3D地图的样子

3D地图.png

俯视图如下

俯视图.png

这是建模工具的展现方式。

我们在游戏里怎么通过2D视图来让玩家感受3D的空间呢?

重点来了,这也是这款游戏的核心玩点

玩家的主视角是当前高度的平面俯视图,通过地图的颜色来让玩家区分自己所处的地形:

地面:可自由移动(淡黄色表示)

阻挡:不可移动(灰色表示)

塌陷:走上去会下落到下一层,直到到达某一层的地面(黑色表示)

玩家可以自由切换到其他相邻高度,高度发生变化,平面俯视图也会相应变化

如图所示的一个空间区域(俯视图)

淡蓝色阻挡高度为1,深蓝色阻挡高度为2

地图.png

它在游戏中的表现如下,一共有3种不同的高度表现

20190124_154914.gif

现在我们用代码来实现这个空间

目前的版本不存在中空的“洞穴”,也就是说所有的阻挡如果存在,则一定是从第一层开始连续向上堆叠形成

在这个前提下我们就可以按下面的方式来定义一个空间内的阻挡:

a.在第一层的矩形投影

b.在空间上的高度H_block

而最后的活动空间,就是这些阻挡的“并集”以外的区域

class Block(object):

def __init__(self,x,y,w,h,lv):#构造函数的参数表示投影矩形(x,y,w,h),以及阻挡的高度lv

self.rect = Rect(x,y,w,h)

self.lv = lv

for k in range(lv):#遍历[0,lv-1]层

for i in range(y,y+h):

for j in range(x,x+w):

grid_map[k][i][j]=2#在[0,lv-1]层形成阻挡

for i in range(y,y+h):

for j in range(x,x+w):

if(0 == grid_map[lv][i][j]):#注意不能把已有的阻挡重算为平地

grid_map[lv][i][j]=1#在lv层形成地面

我们需要在屏幕上绘制出这3种地形:

障碍物:比当前高度(H_cur)高的阻挡(H_block>H_cur)

平地:等于当前高度的阻挡(H_block==H_cur)

塌陷区:没有阻挡的区域或阻挡高度小于当前高度(H_block

def DrawMap(lv):
if lv > H :
return
for b in block_list:
if(b.lv > lv):
pygame.draw.rect(screen, color2, b.rect)
elif b.lv == lv :
pygame.draw.rect(screen, color1, b.rect)

最后我们再来看怎么操作玩家移动

在当前高度的移动与二维平面的移动一致(在上一篇中已经有讲述)

所以只需要实现如何改变玩家的高度

我的设计是这样的:

按z键后开始浮空,并立即飞到上一层,浮空状态时走到塌陷区不会下落

按x键后取消浮空,下落到能到达的最高地面

所以可以这样定义一个玩家类:

class Robot(object):
def __init__(self):
self.x = 10
self.y = 10
self.z = 0
self.is_fly = 0#表示是否是浮空状态

再针对玩家的按键做对应的处理,就可以实现3维空间内的运动了

if (key_press[K_z]):
self.is_fly = 1
self.z += 1
elif (key_press[K_x]):
self.is_fly = 0

完整的代码可以从这里获取