在pygame中添加滚动到平台游戏

时间:2013-01-16 08:37:09

标签: python scroll pygame

好的,所以我在下面列出了我的项目的代码,我只是在制作平台游戏时尝试使用pygame。我试图弄清楚如何在玩家之后进行一些非常简单的滚动,因此玩家是相机的中心,它会反弹/跟随他。任何人都可以帮助我吗?

import pygame
from pygame import *

WIN_WIDTH = 800
WIN_HEIGHT = 640
HALF_WIDTH = int(WIN_WIDTH / 2)
HALF_HEIGHT = int(WIN_HEIGHT / 2)

DISPLAY = (WIN_WIDTH, WIN_HEIGHT)
DEPTH = 32
FLAGS = 0
CAMERA_SLACK = 30

def main():
    global cameraX, cameraY
    pygame.init()
    screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH)
    pygame.display.set_caption("Use arrows to move!")
    timer = pygame.time.Clock()

    up = down = left = right = running = False
    bg = Surface((32,32))
    bg.convert()
    bg.fill(Color("#000000"))
    entities = pygame.sprite.Group()
    player = Player(32, 32)
    platforms = []

    x = y = 0
    level = [
        "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",
        "P                                P",
        "P                                P",
        "P                                P",
        "P                                P",
        "P                                P",
        "P                                P",
        "P                                P",
        "P       PPPPPPPPPPP              P",
        "P                                P",
        "P                                P",
        "P                                P",
        "P                                P",
        "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",]
    # build the level
    for row in level:
        for col in row:
            if col == "P":
                p = Platform(x, y)
                platforms.append(p)
                entities.add(p)
            if col == "E":
                e = ExitBlock(x, y)
                platforms.append(e)
                entities.add(e)
            x += 32
        y += 32
        x = 0

    entities.add(player)

    while 1:
        timer.tick(60)

        for e in pygame.event.get():
            if e.type == QUIT: raise SystemExit, "QUIT"
            if e.type == KEYDOWN and e.key == K_ESCAPE:
                raise SystemExit, "ESCAPE"
            if e.type == KEYDOWN and e.key == K_UP:
                up = True
            if e.type == KEYDOWN and e.key == K_DOWN:
                down = True
            if e.type == KEYDOWN and e.key == K_LEFT:
                left = True
            if e.type == KEYDOWN and e.key == K_RIGHT:
                right = True
            if e.type == KEYDOWN and e.key == K_SPACE:
                running = True

            if e.type == KEYUP and e.key == K_UP:
                up = False
            if e.type == KEYUP and e.key == K_DOWN:
                down = False
            if e.type == KEYUP and e.key == K_RIGHT:
                right = False
            if e.type == KEYUP and e.key == K_LEFT:
                left = False
            if e.type == KEYUP and e.key == K_RIGHT:
                right = False

        # draw background
        for y in range(32):
            for x in range(32):
                screen.blit(bg, (x * 32, y * 32))

        # update player, draw everything else
        player.update(up, down, left, right, running, platforms)
        entities.draw(screen)

        pygame.display.update()

class Entity(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)

class Player(Entity):
    def __init__(self, x, y):
        Entity.__init__(self)
        self.xvel = 0
        self.yvel = 0
        self.onGround = False
        self.image = Surface((32,32))
        self.image.fill(Color("#0000FF"))
        self.image.convert()
        self.rect = Rect(x, y, 32, 32)

    def update(self, up, down, left, right, running, platforms):
        if up:
            # only jump if on the ground
            if self.onGround: self.yvel -= 10
        if down:
            pass
        if running:
            self.xvel = 12
        if left:
            self.xvel = -8
        if right:
            self.xvel = 8
        if not self.onGround:
            # only accelerate with gravity if in the air
            self.yvel += 0.3
            # max falling speed
            if self.yvel > 100: self.yvel = 100
        if not(left or right):
            self.xvel = 0
        # increment in x direction
        self.rect.left += self.xvel
        # do x-axis collisions
        self.collide(self.xvel, 0, platforms)
        # increment in y direction
        self.rect.top += self.yvel
        # assuming we're in the air
        self.onGround = False;
        # do y-axis collisions
        self.collide(0, self.yvel, platforms)

    def collide(self, xvel, yvel, platforms):
        for p in platforms:
            if pygame.sprite.collide_rect(self, p):
                if isinstance(p, ExitBlock):
                    pygame.event.post(pygame.event.Event(QUIT))
                if xvel > 0:
                    self.rect.right = p.rect.left
                    print "collide right"
                if xvel < 0:
                    self.rect.left = p.rect.right
                    print "collide left"
                if yvel > 0:
                    self.rect.bottom = p.rect.top
                    self.onGround = True
                    self.yvel = 0
                if yvel < 0:
                    self.rect.top = p.rect.bottom


class Platform(Entity):
    def __init__(self, x, y):
        Entity.__init__(self)
        self.image = Surface((32, 32))
        self.image.convert()
        self.image.fill(Color("#DDDDDD"))
        self.rect = Rect(x, y, 32, 32)

    def update(self):
        pass

class ExitBlock(Platform):
    def __init__(self, x, y):
        Platform.__init__(self, x, y)
        self.image.fill(Color("#0033FF"))

if __name__ == "__main__":
    main()

5 个答案:

答案 0 :(得分:100)

在绘制实体时,您需要将偏移应用于实体的位置。我们称之为偏移一个camera,因为这是我们希望用此实现的效果。

首先,我们不能使用精灵组的draw函数,因为精灵不需要知道他们的位置(rect)不是他们要去的位置要在屏幕上绘制(最后,我们将继承Group类并重新实现它draw以了解相机,但让我们开始慢一点。


让我们首先创建一个Camera类来保存我们想要应用于实体位置的偏移量的状态:

class Camera(object):
    def __init__(self, camera_func, width, height):
        self.camera_func = camera_func
        self.state = Rect(0, 0, width, height)

    def apply(self, target):
        return target.rect.move(self.state.topleft)

    def update(self, target):
        self.state = self.camera_func(self.state, target.rect)

这里要注意的一些事情:

我们需要存储相机的位置,以及像素的宽度和高度(因为我们想要停止在关卡的边缘滚动)。我使用Rect来存储所有这些信息,但您可以轻松地使用某些字段。

使用Rectapply函数中派上用场。这是我们在屏幕上重新计算实体位置以应用滚动的地方。

主循环的每次迭代,我们需要更新摄像机的位置,因此有update函数。它只是通过调用camera_func函数来改变状态,这将为我们做所有辛勤工作。我们稍后会实施它。

让我们创造一个相机的实例:

for row in level:
    ...

total_level_width  = len(level[0])*32 # calculate size of level in pixels
total_level_height = len(level)*32    # maybe make 32 an constant
camera = Camera(*to_be_implemented*, total_level_width, total_level_height)

entities.add(player)
... 

并改变我们的主循环:

# draw background
for y in range(32):
    ...

camera.update(player) # camera follows player. Note that we could also follow any other sprite

# update player, draw everything else
player.update(up, down, left, right, running, platforms)
for e in entities:
    # apply the offset to each entity.
    # call this for everything that should scroll,
    # which is basically everything other than GUI/HUD/UI
    screen.blit(e.image, camera.apply(e)) 

pygame.display.update()

我们的相机类已经非常灵活,但很简单。它可以使用不同类型的滚动(通过提供不同的camera_func函数),它可以跟随任何仲裁精灵,而不仅仅是玩家。你甚至可以在运行时更改它。

现在执行camera_func。一个简单的方法是在屏幕上将玩家(或我们想要遵循的任何实体)居中,并且实施是直截了当的:

def simple_camera(camera, target_rect):
    l, t, _, _ = target_rect # l = left,  t = top
    _, _, w, h = camera      # w = width, h = height
    return Rect(-l+HALF_WIDTH, -t+HALF_HEIGHT, w, h)

我们只占用target的位置,并添加总屏幕尺寸的一半。您可以通过以下方式创建相机来尝试:

camera = Camera(simple_camera, total_level_width, total_level_height)

到目前为止,这么好。但也许我们不希望看到黑色背景之外的水平?怎么样:

def complex_camera(camera, target_rect):
    # we want to center target_rect
    x = -target_rect.center[0] + WIN_WIDTH/2 
    y = -target_rect.center[1] + WIN_HEIGHT/2
    # move the camera. Let's use some vectors so we can easily substract/multiply
    camera.topleft += (pygame.Vector2((x, y)) - pygame.Vector2(camera.topleft)) * 0.06 # add some smoothness coolnes
    # set max/min x/y so we don't see stuff outside the world
    camera.x = max(-(camera.width-WIN_WIDTH), min(0, camera.x))
    camera.y = max(-(camera.height-WIN_HEIGHT), min(0, camera.y))

    return camera

在这里,我们只使用min / max函数来确保我们不会将滚动到之外。

通过像这样创建相机来尝试:

camera = Camera(complex_camera, total_level_width, total_level_height)

我们最后的滚动动画有一点动画:

enter image description here

这是完整的代码。注意我改变了一些东西:

  • 级别更高,并有更多平台
  • 使用python 3
  • 使用精灵组来处理相机
  • 重构了一些重复的代码
  • 因为Vector2 / 3现在很稳定,所以使用它们来简化数学
  • 摆脱那个丑陋的事件处理代码并使用pygame.key.get_pressed代替

 #! /usr/bin/python

import pygame
from pygame import *

SCREEN_SIZE = pygame.Rect((0, 0, 800, 640))
TILE_SIZE = 32 
GRAVITY = pygame.Vector2((0, 0.3))

class CameraAwareLayeredUpdates(pygame.sprite.LayeredUpdates):
    def __init__(self, target, world_size):
        super().__init__()
        self.target = target
        self.cam = pygame.Vector2(0, 0)
        self.world_size = world_size
        if self.target:
            self.add(target)

    def update(self, *args):
        super().update(*args)
        if self.target:
            x = -self.target.rect.center[0] + SCREEN_SIZE.width/2
            y = -self.target.rect.center[1] + SCREEN_SIZE.height/2
            self.cam += (pygame.Vector2((x, y)) - self.cam) * 0.05
            self.cam.x = max(-(self.world_size.width-SCREEN_SIZE.width), min(0, self.cam.x))
            self.cam.y = max(-(self.world_size.height-SCREEN_SIZE.height), min(0, self.cam.y))

    def draw(self, surface):
        spritedict = self.spritedict
        surface_blit = surface.blit
        dirty = self.lostsprites
        self.lostsprites = []
        dirty_append = dirty.append
        init_rect = self._init_rect
        for spr in self.sprites():
            rec = spritedict[spr]
            newrect = surface_blit(spr.image, spr.rect.move(self.cam))
            if rec is init_rect:
                dirty_append(newrect)
            else:
                if newrect.colliderect(rec):
                    dirty_append(newrect.union(rec))
                else:
                    dirty_append(newrect)
                    dirty_append(rec)
            spritedict[spr] = newrect
        return dirty            

def main():
    pygame.init()
    screen = pygame.display.set_mode(SCREEN_SIZE.size)
    pygame.display.set_caption("Use arrows to move!")
    timer = pygame.time.Clock()

    level = [
        "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                    PPPPPPPPPPP           P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P    PPPPPPPP                              P",
        "P                                          P",
        "P                          PPPPPPP         P",
        "P                 PPPPPP                   P",
        "P                                          P",
        "P         PPPPPPP                          P",
        "P                                          P",
        "P                     PPPPPP               P",
        "P                                          P",
        "P   PPPPPPPPPPP                            P",
        "P                                          P",
        "P                 PPPPPPPPPPP              P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",]


    platforms = pygame.sprite.Group()
    player = Player(platforms, (TILE_SIZE, TILE_SIZE))
    level_width  = len(level[0])*TILE_SIZE
    level_height = len(level)*TILE_SIZE
    entities = CameraAwareLayeredUpdates(player, pygame.Rect(0, 0, level_width, level_height))

    # build the level
    x = y = 0
    for row in level:
        for col in row:
            if col == "P":
                Platform((x, y), platforms, entities)
            if col == "E":
                ExitBlock((x, y), platforms, entities)
            x += TILE_SIZE
        y += TILE_SIZE
        x = 0

    while 1:

        for e in pygame.event.get():
            if e.type == QUIT: 
                return
            if e.type == KEYDOWN and e.key == K_ESCAPE:
                return

        entities.update()

        screen.fill((0, 0, 0))
        entities.draw(screen)
        pygame.display.update()
        timer.tick(60)

class Entity(pygame.sprite.Sprite):
    def __init__(self, color, pos, *groups):
        super().__init__(*groups)
        self.image = Surface((TILE_SIZE, TILE_SIZE))
        self.image.fill(color)
        self.rect = self.image.get_rect(topleft=pos)

class Player(Entity):
    def __init__(self, platforms, pos, *groups):
        super().__init__(Color("#0000FF"), pos)
        self.vel = pygame.Vector2((0, 0))
        self.onGround = False
        self.platforms = platforms
        self.speed = 8
        self.jump_strength = 10

    def update(self):
        pressed = pygame.key.get_pressed()
        up = pressed[K_UP]
        left = pressed[K_LEFT]
        right = pressed[K_RIGHT]
        running = pressed[K_SPACE]

        if up:
            # only jump if on the ground
            if self.onGround: self.vel.y = -self.jump_strength
        if left:
            self.vel.x = -self.speed
        if right:
            self.vel.x = self.speed
        if running:
            self.vel.x *= 1.5
        if not self.onGround:
            # only accelerate with gravity if in the air
            self.vel += GRAVITY
            # max falling speed
            if self.vel.y > 100: self.vel.y = 100
        print(self.vel.y)
        if not(left or right):
            self.vel.x = 0
        # increment in x direction
        self.rect.left += self.vel.x
        # do x-axis collisions
        self.collide(self.vel.x, 0, self.platforms)
        # increment in y direction
        self.rect.top += self.vel.y
        # assuming we're in the air
        self.onGround = False;
        # do y-axis collisions
        self.collide(0, self.vel.y, self.platforms)

    def collide(self, xvel, yvel, platforms):
        for p in platforms:
            if pygame.sprite.collide_rect(self, p):
                if isinstance(p, ExitBlock):
                    pygame.event.post(pygame.event.Event(QUIT))
                if xvel > 0:
                    self.rect.right = p.rect.left
                if xvel < 0:
                    self.rect.left = p.rect.right
                if yvel > 0:
                    self.rect.bottom = p.rect.top
                    self.onGround = True
                    self.yvel = 0
                if yvel < 0:
                    self.rect.top = p.rect.bottom

class Platform(Entity):
    def __init__(self, pos, *groups):
        super().__init__(Color("#DDDDDD"), pos, *groups)

class ExitBlock(Platform):
    def __init__(self, pos, *groups):
        super().__init__(Color("#0033FF"), pos, *groups)

if __name__ == "__main__":
    main()

答案 1 :(得分:0)

因为你知道,你有一个静态的背景,并且你控制的玩家在他所处的位置上是blitted,你有2个选项可以在中间显示角色。

  1. 如果你的地图是小的,你可以有一个大的img A,并根据玩家的位置得到一个矩形,它将是屏幕的大小。这样,玩家将永远处于中间位置。 Rect.clamp(Rect)或Rect.clamp_ip(Rect)将为您提供帮助。

  2. 另一种方法是在屏幕上有一个不同的元组位置。玩家将在屏幕中心保持恒定值,而背景位置将是玩家位置的负值。

答案 2 :(得分:0)

唯一的方法是将地图中的逻辑位置与屏幕上的物理位置分开。

任何与在屏幕上实际绘制地图相关的代码 - 在您的情况下,精灵的所有.rect属性 - 都必须根据屏幕实际使用的地图部分的偏移量来实现。

例如,您的屏幕可能会显示从左上角的位置(10,10)开始的地图 - 所有显示相关的代码(在上面的情况下都是.rect属性)应该减去屏幕从当前逻辑位置偏移 - (假设该字符位于地图坐标(12,15) - 因此,它应绘制在(12,15) - (10,10) - &gt;(2,5)* BLOCK_SIZE) 在上面的示例中,BLOCK_SIZE被硬编码为32,32,因此您希望在显示器上的物理像素位置(2 * 32,5 * 32)处绘制它。

(提示:避免以这种方式硬编码,在代码开头使其成为常量声明)

答案 3 :(得分:0)

我提出了一种更安全的退出窗口的方法

#! /usr/bin/python

import pygame
from pygame import *
import sys

SCREEN_SIZE = pygame.Rect((0, 0, 800, 640))
TILE_SIZE = 32 
GRAVITY = pygame.Vector2((0, 0.3))

class CameraAwareLayeredUpdates(pygame.sprite.LayeredUpdates):
    def __init__(self, target, world_size):
        super().__init__()
        self.target = target
        self.cam = pygame.Vector2(0, 0)
        self.world_size = world_size
        if self.target:
            self.add(target)

    def update(self, *args):
        super().update(*args)
        if self.target:
            x = -self.target.rect.center[0] + SCREEN_SIZE.width/2
            y = -self.target.rect.center[1] + SCREEN_SIZE.height/2
            self.cam += (pygame.Vector2((x, y)) - self.cam) * 0.05
            self.cam.x = max(-(self.world_size.width-SCREEN_SIZE.width), min(0, self.cam.x))
            self.cam.y = max(-(self.world_size.height-SCREEN_SIZE.height), min(0, self.cam.y))

    def draw(self, surface):
        spritedict = self.spritedict
        surface_blit = surface.blit
        dirty = self.lostsprites
        self.lostsprites = []
        dirty_append = dirty.append
        init_rect = self._init_rect
        for spr in self.sprites():
            rec = spritedict[spr]
            newrect = surface_blit(spr.image, spr.rect.move(self.cam))
            if rec is init_rect:
                dirty_append(newrect)
            else:
                if newrect.colliderect(rec):
                    dirty_append(newrect.union(rec))
                else:
                    dirty_append(newrect)
                    dirty_append(rec)
            spritedict[spr] = newrect
        return dirty            

def exit():
    pygame.quit()
    sys.exit()

def main():
    pygame.init()
    screen = pygame.display.set_mode(SCREEN_SIZE.size)
    pygame.display.set_caption("Use arrows to move!")
    timer = pygame.time.Clock()

    level = [
        "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                    PPPPPPPPPPP           P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P    PPPPPPPP                              P",
        "P                                          P",
        "P                          PPPPPPP         P",
        "P                 PPPPPP                   P",
        "P                                          P",
        "P         PPPPPPP                          P",
        "P                                          P",
        "P                     PPPPPP               P",
        "P                                          P",
        "P   PPPPPPPPPPP                            P",
        "P                                          P",
        "P                 PPPPPPPPPPP              P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",]


    platforms = pygame.sprite.Group()
    player = Player(platforms, (TILE_SIZE, TILE_SIZE))
    level_width  = len(level[0])*TILE_SIZE
    level_height = len(level)*TILE_SIZE
    entities = CameraAwareLayeredUpdates(player, pygame.Rect(0, 0, level_width, level_height))

    # build the level
    x = y = 0
    for row in level:
        for col in row:
            if col == "P":
                Platform((x, y), platforms, entities)
            if col == "E":
                ExitBlock((x, y), platforms, entities)
            x += TILE_SIZE
        y += TILE_SIZE
        x = 0

    while 1:

        for e in pygame.event.get():
            if e.type == QUIT: 
                exit()
            if e.type == KEYDOWN and e.key == K_ESCAPE:
                exit()

        entities.update()

        screen.fill((0, 0, 0))
        entities.draw(screen)
        pygame.display.update()
        timer.tick(60)

class Entity(pygame.sprite.Sprite):
    def __init__(self, color, pos, *groups):
        super().__init__(*groups)
        self.image = Surface((TILE_SIZE, TILE_SIZE))
        self.image.fill(color)
        self.rect = self.image.get_rect(topleft=pos)

class Player(Entity):
    def __init__(self, platforms, pos, *groups):
        super().__init__(Color("#0000FF"), pos)
        self.vel = pygame.Vector2((0, 0))
        self.onGround = False
        self.platforms = platforms
        self.speed = 8
        self.jump_strength = 10

    def update(self):
        pressed = pygame.key.get_pressed()
        up = pressed[K_UP]
        left = pressed[K_LEFT]
        right = pressed[K_RIGHT]
        running = pressed[K_SPACE]

        if up:
            # only jump if on the ground
            if self.onGround: self.vel.y = -self.jump_strength
        if left:
            self.vel.x = -self.speed
        if right:
            self.vel.x = self.speed
        if running:
            self.vel.x *= 1.5
        if not self.onGround:
            # only accelerate with gravity if in the air
            self.vel += GRAVITY
            # max falling speed
            if self.vel.y > 100: self.vel.y = 100
        print(self.vel.y)
        if not(left or right):
            self.vel.x = 0
        # increment in x direction
        self.rect.left += self.vel.x
        # do x-axis collisions
        self.collide(self.vel.x, 0, self.platforms)
        # increment in y direction
        self.rect.top += self.vel.y
        # assuming we're in the air
        self.onGround = False;
        # do y-axis collisions
        self.collide(0, self.vel.y, self.platforms)

    def collide(self, xvel, yvel, platforms):
        for p in platforms:
            if pygame.sprite.collide_rect(self, p):
                if isinstance(p, ExitBlock):
                    pygame.event.post(pygame.event.Event(QUIT))
                if xvel > 0:
                    self.rect.right = p.rect.left
                if xvel < 0:
                    self.rect.left = p.rect.right
                if yvel > 0:
                    self.rect.bottom = p.rect.top
                    self.onGround = True
                    self.yvel = 0
                if yvel < 0:
                    self.rect.top = p.rect.bottom

class Platform(Entity):
    def __init__(self, pos, *groups):
        super().__init__(Color("#DDDDDD"), pos, *groups)

class ExitBlock(Platform):
    def __init__(self, pos, *groups):
        super().__init__(Color("#0033FF"), pos, *groups)

if __name__ == "__main__":
    main()

答案 4 :(得分:0)

不幸的是,Pygame 没有针对此问题的内置解决方案。 Pygame 使用 pygame.sprite.Sprite 组织的 pygame.sprite.Groups 对象。 Sprites 的属性 .rect 用于绘制对象以及对象之间的碰撞测试。没有内置功能可以在绘制前将对象坐标转换为屏幕坐标。
作为对 Pygame 开发人员的建议:最好在方法 pygame.sprite.Group.draw 中为相机 offset 提供一个可选参数。

有不同的方法:

  • 您可以向相反方向移动场景中的任何对象,而不是移动玩家。这是所有方法中最糟糕的。我强烈建议不要这样做。 每次添加新对象时,您都需要确保它随着玩家移动而移动。处理对象动画或浮点精度可能会变成一场噩梦。

  • 创建世界的虚拟屏幕大小,并在虚拟屏幕上绘制整个屏幕。在每一帧结束时,屏幕上会显示地图的一部分。

    virtual_screen = pygame.Surface((map_width, map_height))
    

    有两种可能。您可以通过指定 area 参数,直接在屏幕上blit 虚拟屏幕的一个区域:

    camera_area = pygame.Rect(camera_x, camera_y, camera_width, camera_height)
    screen.blit(virtual_screen, (0, 0), camera_area)
    

    另一种可能性是使用subsurface方法定义一个直接链接到源表面的次表面:

    camera_area = pygame.Rect(camera_x, camera_y, camera_width, camera_height)
    camera_subsurf = source_surf.subsurface(camera_area)
    
    screen.blit(camera_subsurf, (0, 0))
    

    这种方法的缺点是它可能有非常大的内存占用。如果虚拟屏幕很大,游戏就会滞后。这种解决方案只适用于游戏区域的大小不比屏幕大多少的情况。根据经验,如果播放区域是屏幕大小的两倍以上,则不应该走这条路(我说的是该区域大小的两倍,而不是其宽度和高度的两倍长度) .

  • 对于大型游乐区,唯一可以使用的方法是在绘制之前向对象添加偏移量:

    offset_x = -camera_x
    offset_y = -camera_y
    for object in objects:
        screen.blit(object.image, (object.rect.x + offset_x, object.rect.y + offset_y))
    

    很遗憾在这种情况下不能直接使用 pygame.sprite.Group.draw。评价很高的答案中详细介绍了这种方法。
    或者,您可以在绘制之前移动所有精灵:

    all_sprites = pygame.sprite.Group()
    
    for sprite in all_sprites:
        all_sprites.rect.move_ip(-camera_x, -camera_y)
    all_sprites.draw(screen)    
    for sprite in all_sprites:
        all_sprites.rect.move_ip(camera_x, camera_y)
    

最后关于脏机制和部分屏幕更新的评论:玩家一移动,整个屏幕都是脏的,需要更新。因此,您是否应该在部分更新机制上投入资源是值得怀疑的。这些算法也需要时间来运行。在高动态场景下,算法的结果是全部更新。