临摹微笑
好的,我会尽量简化(我假设有基本的 pygame 知识)。首先,让我们从一些基本的东西开始。一个可以在世界各地移动的小精灵:import pygameimport randomclass Player(pygame.sprite.Sprite): def __init__(self): super().__init__() self.image = pygame.Surface((32, 32)) self.image.fill(pygame.Color('dodgerblue')) self.rect = self.image.get_rect() self.pos = pygame.Vector2((100, 200)) def update(self, events, dt): pressed = pygame.key.get_pressed() move = pygame.Vector2((0, 0)) if pressed[pygame.K_w]: move += (0, -1) if pressed[pygame.K_a]: move += (-1, 0) if pressed[pygame.K_s]: move += (0, 1) if pressed[pygame.K_d]: move += (1, 0) if move.length() > 0: move.normalize_ip() self.pos += move*(dt/5) self.rect.center = self.posdef main(): pygame.init() screen = pygame.display.set_mode((500, 500)) clock = pygame.time.Clock() dt = 0 player = Player() sprites = pygame.sprite.Group(player) background = screen.copy() background.fill((30, 30, 30)) for _ in range(1000): x, y = random.randint(0, 1000), random.randint(0, 1000) pygame.draw.rect(background, pygame.Color('green'), (x, y, 2, 2)) while True: events = pygame.event.get() for e in events: if e.type == pygame.QUIT: return sprites.update(events, dt) screen.blit(background, (0, 0)) sprites.draw(screen) pygame.display.update() dt = clock.tick(60)if __name__ == '__main__': main()到目前为止没有什么疯狂的。那么,什么是“相机”?它只是我们用来移动整个“世界”的一个x和一个y值(例如,所有不是 UI 的东西)。它是我们游戏对象和屏幕坐标之间的抽象。在我们上面的例子中,当一个游戏对象(玩家或背景)想要在位置 绘制时(x, y),我们在屏幕的这个位置绘制它们。现在,如果我们想要围绕“摄像机”移动,我们只需创建另一个x, y-pair,并将其添加到游戏对象的坐标中以确定屏幕上的实际位置。我们开始区分世界坐标(游戏逻辑认为物体的位置在哪里)和屏幕坐标(物体在屏幕上的实际位置)。这是我们带有“camera”(引号中的“camera”)的示例,因为它实际上只是两个值:import pygameimport randomclass Player(pygame.sprite.Sprite): def __init__(self): super().__init__() self.image = pygame.Surface((32, 32)) self.image.fill(pygame.Color('dodgerblue')) self.rect = self.image.get_rect() self.pos = pygame.Vector2((100, 200)) def update(self, events, dt): pressed = pygame.key.get_pressed() move = pygame.Vector2((0, 0)) if pressed[pygame.K_w]: move += (0, -1) if pressed[pygame.K_a]: move += (-1, 0) if pressed[pygame.K_s]: move += (0, 1) if pressed[pygame.K_d]: move += (1, 0) if move.length() > 0: move.normalize_ip() self.pos += move*(dt/5) self.rect.center = self.posdef main(): pygame.init() screen = pygame.display.set_mode((500, 500)) clock = pygame.time.Clock() dt = 0 player = Player() sprites = pygame.sprite.Group(player) # the "world" is now bigger than the screen # so we actually have anything to move the camera to background = pygame.Surface((1500, 1500)) background.fill((30, 30, 30)) # a camera is just two values: x and y # we use a vector here because it's easier to handle than a tuple camera = pygame.Vector2((0, 0)) for _ in range(3000): x, y = random.randint(0, 1000), random.randint(0, 1000) pygame.draw.rect(background, pygame.Color('green'), (x, y, 2, 2)) while True: events = pygame.event.get() for e in events: if e.type == pygame.QUIT: return # copy/paste because I'm lazy # just move the camera around pressed = pygame.key.get_pressed() camera_move = pygame.Vector2() if pressed[pygame.K_UP]: camera_move += (0, 1) if pressed[pygame.K_LEFT]: camera_move += (1, 0) if pressed[pygame.K_DOWN]: camera_move += (0, -1) if pressed[pygame.K_RIGHT]: camera_move += (-1, 0) if camera_move.length() > 0: camera_move.normalize_ip() camera += camera_move*(dt/5) sprites.update(events, dt) # before drawing, we shift everything by the camera's x and y values screen.blit(background, camera) for s in sprites: screen.blit(s.image, s.rect.move(*camera)) pygame.display.update() dt = clock.tick(60)if __name__ == '__main__': main()现在您可以使用箭头键移动相机。就是这样。我们只是将所有内容稍微移动一下,然后再将其传输到屏幕上。更完整的例子(支持精灵,停在世界边缘,平滑移动),请看这个问题。对于使用 pymunk:它只是有效。它不受将东西绘制到另一个位置的影响,因为它适用于世界坐标,而不是屏幕坐标。唯一的缺陷是 pymunk 的 y 轴与 pygame 的 y 轴相比发生了翻转,但您可能已经知道这一点。下面是一个例子:import pygameimport randomimport pymunkclass Player(pygame.sprite.Sprite): def __init__(self, space): super().__init__() self.space = space self.image = pygame.Surface((32, 32)) self.image.fill(pygame.Color('dodgerblue')) self.rect = self.image.get_rect() self.pos = pygame.Vector2((100, 200)) self.body = pymunk.Body(1,1666) self.body.position = self.pos self.poly = pymunk.Poly.create_box(self.body) self.space.add(self.body, self.poly) def update(self, events, dt): pressed = pygame.key.get_pressed() move = pygame.Vector2((0, 0)) if pressed[pygame.K_w]: move += (0, 1) if pressed[pygame.K_a]: move += (-1, 0) if pressed[pygame.K_s]: move += (0, -1) if pressed[pygame.K_d]: move += (1, 0) if move.length() > 0: move.normalize_ip() self.body.apply_impulse_at_local_point(move*5) # if you used pymunk before, you'll probably already know # that you'll have to invert the y-axis to convert between # the pymunk and the pygame coordinates. self.pos = pygame.Vector2(self.body.position[0], -self.body.position[1]+500) self.rect.center = self.posdef main(): pygame.init() screen = pygame.display.set_mode((500, 500)) clock = pygame.time.Clock() dt = 0 space = pymunk.Space() space.gravity = 0,-100 player = Player(space) sprites = pygame.sprite.Group(player) # the "world" is now bigger than the screen # so we actually have anything to move the camera to background = pygame.Surface((1500, 1500)) background.fill((30, 30, 30)) # a camera is just two values: x and y # we use a vector here because it's easier to handle than a tuple camera = pygame.Vector2((0, 0)) for _ in range(3000): x, y = random.randint(0, 1000), random.randint(0, 1000) pygame.draw.rect(background, pygame.Color('green'), (x, y, 2, 2)) while True: events = pygame.event.get() for e in events: if e.type == pygame.QUIT: return # copy/paste because I'm lazy # just move the camera around pressed = pygame.key.get_pressed() camera_move = pygame.Vector2() if pressed[pygame.K_UP]: camera_move += (0, 1) if pressed[pygame.K_LEFT]: camera_move += (1, 0) if pressed[pygame.K_DOWN]: camera_move += (0, -1) if pressed[pygame.K_RIGHT]: camera_move += (-1, 0) if camera_move.length() > 0: camera_move.normalize_ip() camera += camera_move*(dt/5) sprites.update(events, dt) # before drawing, we shift everything by the camera's x and y values screen.blit(background, camera) for s in sprites: screen.blit(s.image, s.rect.move(*camera)) pygame.display.update() dt = clock.tick(60) space.step(dt/1000)if __name__ == '__main__': main()请注意,当您使用 时pymunk.Space.debug_draw,您将无法将世界坐标转换为屏幕坐标,因此最好将 pymunk 的东西简单地绘制到另一个Surface,并将其转换为Surface.这是pygame_util_demo.py带有移动相机的 pymunk :import sysimport pygamefrom pygame.locals import *import pymunkfrom pymunk.vec2d import Vec2dimport pymunk.pygame_utilimport shapes_for_draw_demosdef main(): pygame.init() screen = pygame.display.set_mode((1000,700)) pymunk_layer = pygame.Surface((1000,700)) pymunk_layer.set_colorkey((12,12,12)) pymunk_layer.fill((12,12,12)) camera = pygame.Vector2((0, 0)) clock = pygame.time.Clock() font = pygame.font.SysFont("Arial", 16) space = pymunk.Space() captions = shapes_for_draw_demos.fill_space(space) # Info color = pygame.color.THECOLORS["black"] options = pymunk.pygame_util.DrawOptions(pymunk_layer) while True: for event in pygame.event.get(): if event.type == QUIT or \ event.type == KEYDOWN and (event.key in [K_ESCAPE, K_q]): return elif event.type == KEYDOWN and event.key == K_p: pygame.image.save(screen, "pygame_util_demo.png") # copy/paste because I'm lazy pressed = pygame.key.get_pressed() camera_move = pygame.Vector2() if pressed[pygame.K_UP]: camera_move += (0, 1) if pressed[pygame.K_LEFT]: camera_move += (1, 0) if pressed[pygame.K_DOWN]: camera_move += (0, -1) if pressed[pygame.K_RIGHT]: camera_move += (-1, 0) if camera_move.length() > 0: camera_move.normalize_ip() camera += camera_move*5 screen.fill(pygame.color.THECOLORS["white"]) pymunk_layer.fill((12,12,12)) space.debug_draw(options) screen.blit(pymunk_layer, camera) screen.blit(font.render("Demo example of pygame_util.DrawOptions()", 1, color), (205, 680)) for caption in captions: x, y = caption[0] y = 700 - y screen.blit(font.render(caption[1], 1, color), camera + (x,y)) pygame.display.flip() clock.tick(30)if __name__ == '__main__': sys.exit(main())