3.5    滚动的小问题

现在还遗留了一个问题,当滚动地图时,如果马里奥移动到水管的左侧时,如3‑7所示。

37马里奥位于水管左侧

此时,继续滚动地图,马里奥会嗖的一下自己跳上水管,如3‑8所示,但实际上我们并没有按跳跃键。

38 马里奥自己跳上了水管

         让我们仔细研究一下为什么会发生这种情况。

在第一帧中,地图滚动,马里奥移动到水管左侧,如3‑7的位置。

第二帧包含以下步骤:

l  根据当前mapViewFrom变量的值,绘制地图。

l  马里奥处理按键,向右移动。因为马里奥超过屏幕的一半位置,直接修改了mapViewFrom变量的值来移动屏幕范围。

l  马里奥处理重力,需要将马里奥的坐标转换为地图坐标,然后进行碰撞检测。由于上一步修改了mapViewFrom的值,所以转换后的地图坐标,实际是马里奥向右移动了一步的坐标。马里奥的右侧是水管,所以检测到碰撞,但是,这个碰撞检测是不区分方向的,无论是横着撞到水管还是竖着撞到水管,结果都是一样的。这时认为马里奥在掉落时碰到了水管,所以将马里奥放到水管顶部。

通过分析可见,第一个问题是在处理当前帧时提前修改了mapViewFrom的值,影响了后续的坐标转换。解决办法是暂时不修改变量值,只做一个标记,直到本帧结束再修改变量值。第二个问题是滚动地图时直接滚动,而没有判断右侧是否有障碍物。为了解决这两个问题,需要做一些修改,代码如下所示:

03\06\Mario.py

# 更新

def update(self, keys):

 

# 步伐,先恢复默认值

self.walkStep = self.walkStepDefault

self.jumpStep = self.jumpStepDefault

self.dropStep = self.dropStepDefault

 

# 是否滚动地图

isScrollMap = False

 

# 按键状态

if keys[pygame.K_LEFT]:

    print("按了左方向键")

 

    # 窗口边界判断

    if self.rect.x - self.walkStep <= 0:

        self.walkStep = self.rect.x

 

    # 下一步预判断

    nextStepRect = self.rect.move(-1 * self.walkStep, 0)

    pipeIndex = screenToMap(nextStepRect).collidelist(Globals.game.objectArray)  # 是否碰撞

    if pipeIndex != -1:

        self.walkStep = self.rect.x - mapToScreen(Globals.game.objectArray[pipeIndex]).right

    self.rect = self.rect.move(-1 * self.walkStep, 0)

if keys[pygame.K_RIGHT]:

    print("按了右方向键")

 

    # 超过屏幕中线才滚动地图,地图末尾右半屏也不滚动

    if self.rect.x >= SCREEN_WIDTH / 2 and screenToMap(

            self.rect).x < Globals.game.mapViewFromMax + SCREEN_WIDTH / 2:

 

        # 如果地图滚动后,有碰撞发生,则不能滚动

        # 地图向左滚动,相当于马里奥向右移动。

        # 这里不能修改mapViewFrom的值

        nextStepRect = self.rect.move(self.walkStep, 0)

        pipeIndex = screenToMap(nextStepRect).collidelist(Globals.game.objectArray)

        if pipeIndex == -1:

            isScrollMap = True

    else:

        # 窗口边界判断

        if self.rect.right + self.walkStep >= SCREEN_WIDTH:

            self.walkStep = SCREEN_WIDTH - self.rect.right

 

        # 下一步预判断

        nextStepRect = self.rect.move(self.walkStep, 0)

        pipeIndex = screenToMap(nextStepRect).collidelist(Globals.game.objectArray)

        if pipeIndex != -1:

            self.walkStep = mapToScreen(Globals.game.objectArray[pipeIndex]).x - self.rect.right

        self.rect = self.rect.move(self.walkStep, 0)

if keys[pygame.K_g]:

    print("按了跳跃键")

 

    # 窗口边界判断

    if self.rect.y - self.jumpStep <= 0:

        self.jumpStep = self.rect.y

 

    # 下一步预判断

    nextStepRect = self.rect.move(0, -1 * self.jumpStep)

    pipeIndex = screenToMap(nextStepRect).collidelist(Globals.game.objectArray)

    if pipeIndex != -1:

        self.jumpStep = (mapToScreen(Globals.game.objectArray[pipeIndex]).bottom - self.rect.y) * -1

    self.rect = self.rect.move(0, -1 * self.jumpStep)

 

# ----重力影响----

# 下一步预判断

nextStepRect = self.rect.move(0, self.dropStep)

pipeIndex = screenToMap(nextStepRect).collidelist(Globals.game.objectArray)

if pipeIndex != -1:

    self.dropStep = mapToScreen(Globals.game.objectArray[pipeIndex]).y - self.rect.bottom

self.rect = self.rect.move(0, self.dropStep)

 

# 显示马里奥

pygame.draw.rect(self.screen, COLOR_BLACK, self.rect, 1)  # 边框

pygame.draw.rect(self.screen, COLOR_WHITE, self.rect.inflate(-2, -2), 0)  # 边框内部白色填充

pygame.draw.rect(self.screen, COLOR_BLACK, self.rect.inflate(-10, -10), 0)  # 中心黑色填充

 

# 滚动地图

if isScrollMap:

    Globals.game.scrollMap(self.walkStep)

由于不能修改mapViewFrom的值,所以让马里奥移动后进行碰撞检测,效果与滚动地图来检测是一样的。注意nextStepRect变量是一个临时变量,只是用来做碰撞检测,实际并没有修改马里奥的Rect对象。运行程序,会发现马里奥跳上水管的问题解决了。