Добавить прокрутку на платформер в 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", "PP", "PP", "PP", "PP", "PP", "PP", "PP", "P PPPPPPPPPPP P", "PP", "PP", "PP", "PP", "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() 

    3 Solutions collect form web for “Добавить прокрутку на платформер в pygame”

    При их создании необходимо применить смещение к позиции ваших объектов. Давайте назовем это смещение camera , так как это тот эффект, который мы хотим достичь с этим.

    Во-первых, мы не можем (и не должны) использовать функцию draw группы спрайтов, так как спрайты не должны знать, что их позиция ( rect ) – это не позиция, которую они собираются рисовать на экране (конечно, мы могли бы подклассифицировать класс Group и переопределить ее, чтобы быть в курсе камеры, но ради обучения давайте будем здесь явными).


    Начнем с создания класса 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 для хранения всех этих данных, но вы можете легко использовать некоторые поля.

    Использование функции Rect полезно в функции apply . Здесь мы переучитываем положение объекта на экране, чтобы применить прокрутку.

    Один раз за итерацию основного цикла нам нужно обновить положение камеры, следовательно, есть функция 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): l, t, _, _ = target_rect _, _, w, h = camera l, t, _, _ = -l+HALF_WIDTH, -t+HALF_HEIGHT, w, h # center player l = min(0, l) # stop scrolling at the left edge l = max(-(camera.width-WIN_WIDTH), l) # stop scrolling at the right edge t = max(-(camera.height-WIN_HEIGHT), t) # stop scrolling at the bottom t = min(0, t) # stop scrolling at the top return Rect(l, t, w, h) 

    Здесь мы просто используем функции min / max чтобы гарантировать, что мы не прокручиваем внешний уровень.

    Попробуйте, создав свою камеру следующим образом:

     camera = Camera(complex_camera, total_level_width, total_level_height) 

    Немного анимация нашей новой прокрутки в действии:

    введите описание изображения здесь

    Вот полный код еще раз (обратите внимание, что я немного изменил ваш уровень, чтобы быть больше и иметь еще несколько платформ):

     #! /usr/bin/python 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 = [ "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP", "PP", "PP", "PP", "P PPPPPPPPPPP P", "PP", "PP", "PP", "P PPPPPPPP P", "PP", "P PPPPPPP P", "P PPPPPP P", "PP", "P PPPPPPP P", "PP", "P PPPPPP P", "PP", "P PPPPPPPPPPP P", "PP", "P PPPPPPPPPPP P", "PP", "PP", "PP", "PP", "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",] # 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 total_level_width = len(level[0])*32 total_level_height = len(level)*32 camera = Camera(complex_camera, total_level_width, total_level_height) 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 # draw background for y in range(32): for x in range(32): screen.blit(bg, (x * 32, y * 32)) camera.update(player) # update player, draw everything else player.update(up, down, left, right, running, platforms) for e in entities: screen.blit(e.image, camera.apply(e)) pygame.display.update() 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) def simple_camera(camera, target_rect): l, t, _, _ = target_rect _, _, w, h = camera return Rect(-l+HALF_WIDTH, -t+HALF_HEIGHT, w, h) def complex_camera(camera, target_rect): l, t, _, _ = target_rect _, _, w, h = camera l, t, _, _ = -l+HALF_WIDTH, -t+HALF_HEIGHT, w, h l = min(0, l) # stop scrolling at the left edge l = max(-(camera.width-WIN_WIDTH), l) # stop scrolling at the right edge t = max(-(camera.height-WIN_HEIGHT), t) # stop scrolling at the bottom t = min(0, t) # stop scrolling at the top return Rect(l, t, w, h) 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() 

    Поскольку вы знаете, у вас есть статический фон, и игрок, которым вы управляете, близок в позиции, в которой он находится, у вас есть 2 варианта, чтобы всегда показывать персонажа посередине.

    1. Если вы на карте достаточно мал, вы можете получить большой img A и получить прямоугольник, основанный на позиции игрока, который будет размером с экран. Таким образом, игрок всегда будет посередине. Rect.clamp (Rect) или Rect.clamp_ip (Rect) помогут вам в этом.

    2. Другой подход заключается в том, чтобы иметь разные кортежи для отображения на экране. Игрок будет иметь постоянное значение в центре экрана, в то время как положение фона будет отрицательным для позиции игрока.

    Единственный способ сделать это – отделить логические позиции на карте от физических положений на экране.

    Любой код, связанный с фактическим отображением вашей карты на экране – в вашем случае все .rect атрибуты ваших спрайтов – должен сделать это на основе смещения какой части карты, которую фактически использует экран.

    Например, на вашем экране может отображаться ваша карта, начиная с позиции (10,10) в левом верхнем углу – все связанные с ними коды кода (которые в приведенном выше примере являются .rect атрибутами) должны вычесть смещение экрана от текущей логической позиции – (например, символ находится в координатах карты (12,15) – поэтому его следует нарисовать на (12,15) – (10, 10) -> (2, 5) * BLOCK_SIZE). В вашем примере выше BLOCK_SIZE является жестко запрограммированным до 32,32, поэтому вы хотите нарисовать его на физическом пиксельном положении (2 * 32, 5 * 32) на дисплее)

    (подсказка: избегайте такого рода hardcoding, сделайте это константной декларацией в начале вашего кода)

     
    Interesting Posts for Van-Lav

    Как сделать перенаправление вывода оболочки (>), когда скрипт все еще запущен?

    Нормализовать гистограмму (яркость и контрастность) набора изображений с использованием библиотеки изображений Python (PIL)

    Какие исключения может вызвать функция Python?

    Как открыть текстовый файл юникода внутри zip?

    Почему «импортировать модуль», а затем «из модуля импорта пакетов» загружает модуль снова?

    Можно ли запустить скрипт Python в качестве службы в Windows? Если возможно, как?

    Как преобразовать этот тип данных <hdf5 object reference> в нечто более читаемое в python?

    Почему я не могу установить соединение с rabbitMQ с помощью python?

    Запустить скрипт python внутри скрипта powershell

    с помощью SDK Google App Engine в pycharm

    Показатели Matplotlib не работают после диалога с файлом Tkinter

    Как сгладить дерево синтаксического разбора и сохранить в строке для дальнейших строковых операций python nltk

    соответствие символов Unicode в регулярных выражениях python

    Учитывая список фрагментов, как мне разделить их на них?

    Ошибка импорта в zope.interface.registry в python

    Python - лучший язык программирования в мире.