Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Пишем платформер на python, применяя pygame. Часть 2 подчасть 2. Редактор ярусов

Anna | 16.06.2014 | нет комментариев

Здравствуй, друзья! Сегодня мы наконец-то доделаем нашего мариобоя. Предисловие здесь и здесь. Вот только мы не будем изобретать свой велосипед в виде редактора ярусов, а воспользуемся готовым сильныминструментом. За знакомство с которым я признателен господам(товарищам) sourcerer и Tarvitz

Отчего так?

На это есть несколько причин

  • Комфортный редактор ярусов не пишется за 5 минут, отменнее потратим это время на допиливание самой игры
  • Больше легкий метод добавления в игру различных на вид типов блоков
  • Tiled map editor является универсальным инструментом для 2d игр, разобравшись с ним однажды, мы приобретаем навык генерации ярусов для различных игр, написанных на различных языках и спецтехнологиях

Создаём неприятности и преграды нашему герою

Про работу с Tiled map editor дозволено почитать, скажем, здесь.
Я же опишу основные моменты создания яруса именно для нашей игры.
Наша карта состоит из минимум 5-ти слоёв:

  1. BackGround — фон
  2. Platforms — блоки, по которым дозволено бегать
  3. DieBlocks -блоки, соприкосновение с которыми вызывает у героя молниеносную гибель
  4. Monsters — слой объектов, здесь наши монстрики, а так же, принцесса и сам герой
  5. Teleports -слой объектов, для чего — ясно по наименованию

Фон

Здесь можете рисовать что желательно и как желательно, тайлы с этого слоя ни как не влияют на героя либо игровой процесс, разве что на эстетический вид игры :)

Блоки, по которым дозволено бегать

На этом слое располагаются тайлы, которые в игре создают объекты класса Platform

Губительно небезопасные блоки

Все тайлы, самостоятельно от внешнего вида, будь то шипы либо кирпичная стена, создают в игре объекты класса BlockDie

Монстры

Это слой объектов, а значит, он не отображает в игре тайлы и всякий объект, добавленный на него, должен владеть какими — нибудь свойствами.
Объекты класса Monster, чей конструктор имеет дальнейший вид

class Monster(sprite.Sprite):
    def __init__(self, x, y, left, up, maxLengthLeft,maxLengthUp):

Непременно обязаны иметь такие свойства, как: leftmaxLeftupmaxUp — заполняемые вручную и xy — передающиеся по расположению объекта.

Объект персонажа должен иметь имя Player

Объект принцессы должен иметь имя Princess

Вид слоя:

Телепорты

Объекты этого слоя обязаны иметь свойства финального назначения перемещения героя: goX и goY

Слой:

Как узнать финальные координаты?

Легко! Навести курсор мыши на то место, куда хотите, Дабы герой телепортировался и посмотреть слева снизу координаты места

Карту в игру

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

import tmxreader # Может загружать tmx файлы
import helperspygame # Преобразует tmx карты в формат  спрайтов pygame

Дальше, очищаем процедуру loadLevel(), мы её перепишем.

loadlevel

def loadLevel(name):
    global playerX, playerY # объявляем всеобщии переменные, это координаты героя
    global total_level_height, total_level_width
    global sprite_layers # все слои карты

    world_map = tmxreader.TileMapParser().parse_decode('%s/%s.tmx' % (FILE_DIR, name)) # загружаем карту
    resources = helperspygame.ResourceLoaderPygame() # инициируем преобразователь карты 
    resources.load(world_map) # и преобразуем карту в внятный pygame формат

    sprite_layers = helperspygame.get_layers_from_map(resources) # получаем все слои карты

    # берем слои по порядку 0 - слой фона, 1- слой блоков, 2 - слой губительных блоков
    # 3 - слой объектов монстров, 4 - слой объектов телепортов
    platforms_layer = sprite_layers[1] 
    dieBlocks_layer = sprite_layers[2]

    for row in range(0, platforms_layer.num_tiles_x): # перебираем все координаты тайлов
        for col in range(0, platforms_layer.num_tiles_y):
            if platforms_layer.content2D[col][row] is not None:
                pf = Platform(row * PLATFORM_WIDTH, col * PLATFORM_WIDTH)# как и раньше создаем объкты класса Platform
                platforms.append(pf)
            if dieBlocks_layer.content2D[col][row] is not None:
                bd = BlockDie(row * PLATFORM_WIDTH, col * PLATFORM_WIDTH)
                platforms.append(bd)

    teleports_layer = sprite_layers[4]
    for teleport in teleports_layer.objects:
        try: # если произойдет оплошность на слое телепортов
            goX = int(teleport.properties["goX"]) * PLATFORM_WIDTH
            goY = int (teleport.properties["goY"]) * PLATFORM_HEIGHT
            x = teleport.x
            y = teleport.y - PLATFORM_HEIGHT
            tp = BlockTeleport(x, y, goX, goY)
            entities.add(tp)
            platforms.append(tp)
            animatedEntities.add(tp)
        except: # то игра не вылетает, а легко выводит сообщение о неудаче
            print(u"Оплошность на слое телепортов")

    monsters_layer = sprite_layers[3]
    for monster in monsters_layer.objects:
        try:
            x = monster.x
            y = monster.y
            if monster.name == "Player":
                playerX = x
                playerY = y - PLATFORM_HEIGHT
            elif monster.name == "Princess":
                pr = Princess(x, y - PLATFORM_HEIGHT)
                platforms.append(pr)
                entities.add(pr)
                animatedEntities.add(pr)
            else:
                up = int(monster.properties["up"])
                maxUp = int(monster.properties["maxUp"])
                left = int(monster.properties["left"])
                maxLeft = int(monster.properties["maxLeft"])
                mn = Monster(x, y - PLATFORM_HEIGHT, left, up, maxLeft, maxUp)
                entities.add(mn)
                platforms.append(mn)
                monsters.add(mn)
        except:
            print(u"Оплошность на слое монстров")

    total_level_width = platforms_layer.num_tiles_x * PLATFORM_WIDTH # Высчитываем фактическую ширину яруса
    total_level_height = platforms_layer.num_tiles_y * PLATFORM_HEIGHT   # высоту
 
Что мы здесь видим?

Начнем с того, что сейчас процедура принимает входной параметр name, тот, что применяется для загрузки карты яруса. Это сделали для того, Дабы сделать переход между ярусами.
Дальше идёт загрузка и реформирование карты, и по тому же тезису, что мы парсили массив с картой, парсим слои с тайлами. Обратите внимание, что сейчас сделанные объекты классов Platform и BlockDie не помещаются в группу entities, а значит, мы их не будет отображать т.е. они будут существовать, но не отображаться. Взамен них мы будет отображать тайлы со слоёв карты.

Продолжим

Сейчас займемся процедурой main
Добавим визуализатор(рендерер) слоёв карты

renderer = helperspygame.RendererPygame() # визуализатор

Для чего он — увидим чуть ниже

Изменим вызов процедуры loadLevel

for lvl in range(1,4):
        loadLevel("map_%s" % lvl)

И дальше, каждый код будет в этом цикле

В блоке итога изображений на экран добавим работу визуализатора

for sprite_layer in sprite_layers: # перебираем все слои
      if not sprite_layer.is_object_group: # и если это не слой объектов
           renderer.render_layer(screen, sprite_layer) # отображаем его
***
 center_offset = camera.reverse(CENTER_OF_SCREEN) # получаем координаты внутри длинного яруса
 renderer.set_camera_position_and_size(center_offset[0], center_offset[1], \
                                                  WIN_WIDTH, WIN_HEIGHT, "center")

Обратите внимание, что renderer выводит свои изображения по центру экрана, внутри передвигающегося фокуса камеры, для этого нам необходимо было добавить процедуру в класс Camera

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)

    def reverse(self, pos):# приобретение внутренних координат из глобальных
        return pos[0] - self.state.left, pos[1] - self.state.top

Уберем то, что перенесли в процедуру loadlevel, добавим немножко нового и получим дальнейший вид:

main

def main():
    pygame.init() # Инициация PyGame, непременная строчка
    screen = pygame.display.set_mode(DISPLAY) # Создаем окошко
    pygame.display.set_caption("Super Mario Boy") # Пишем в шапку
    bg = Surface((WIN_WIDTH, WIN_HEIGHT)) # Создание видимой поверхности
    # будем применять как фон

    renderer = helperspygame.RendererPygame() # визуализатор
    for lvl in range(1,4):
        loadLevel("levels/map_%s" % lvl)
        bg.fill(Color(BACKGROUND_COLOR))     # Заливаем поверхность сплошным цветом

        left = right = False # по умолчанию - стоим
        up = False
        running = False
        try:
            hero = Player(playerX, playerY) # создаем героя по (x,y) координатам
            entities.add(hero)
        except:
            print (u"Не удалось на карте обнаружить героя, взяты координаты по-умолчанию")
            hero = Player(65, 65)
        entities.add(hero)

        timer = pygame.time.Clock()

        camera = Camera(camera_configure, total_level_width, total_level_height)

        while not hero.winner: # Стержневой цикл программы
            timer.tick(60)
            for e in pygame.event.get(): # Обрабатываем события
                if e.type == QUIT:
                    raise SystemExit, "QUIT"
                if e.type == KEYDOWN and e.key == K_UP:
                    up = 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_LSHIFT:
                    running = True

                if e.type == KEYUP and e.key == K_UP:
                    up = 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_LSHIFT:
                    running = False
            for sprite_layer in sprite_layers: # перебираем все слои
                if not sprite_layer.is_object_group: # и если это не слой объектов
                   renderer.render_layer(screen, sprite_layer) # отображаем его

            for e in entities:
                screen.blit(e.image, camera.apply(e))
            animatedEntities.update() # показываеaм анимацию
            monsters.update(platforms) # передвигаем всех монстров
            camera.update(hero) # центризируем камеру касательно персонаж
            center_offset = camera.reverse(CENTER_OF_SCREEN)
            renderer.set_camera_position_and_size(center_offset[0], center_offset[1], \
                                                  WIN_WIDTH, WIN_HEIGHT, "center")
            hero.update(left, right, up, running, platforms) # передвижение
            pygame.display.update()     # обновление и итог всех изменений на экран
            screen.blit(bg, (0, 0))      # Всякую итерацию нужно всё перерисовывать
        for sprite_layer in sprite_layers:
            if not sprite_layer.is_object_group:
                renderer.render_layer(screen, sprite_layer)
        # когда заканчиваем ярус
        for e in entities:
            screen.blit(e.image, camera.apply(e)) # еще раз все перерисовываем
        font=pygame.font.Font(None,38) 
        text=font.render(("Thank you MarioBoy! but our princess is in another level!"), 1,(255,255,255))# выводим надпись
        screen.blit(text, (10,100))
        pygame.display.update()
        time.wait(10000) # ожидаем 10 секунд и позже - переходим на дальнейший ярус
Что здесь увлекательного?

помимо того, что мы разобрали в этой и предыдущей частях статьи, здесь имеется событие, присходящее, когда наш герой касается принцессы. В данный момент мы выводим сообщение о его неудаче и, даём время прочитать его и идет на следующую итерацию, загружая дальнейший ярус.

Вот и всё. Вот так, легко и стремительно мы переделали игру для загрузки ярусов из tmx файлов.

 

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB