Keep your place in this quest

Log in or sign up for free to subscribe, follow lesson progress, and access more learning content.

Теперь, когда вы увидели, как работают персонажи и анимация в Cave, давайте сделаем шаг назад и поймем систему сценариев, которая обычно соединяет все вместе: Python.

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

В этом уроке вы узнаете:

  • Для чего используется Python в Cave.
  • Как работают ресурсы Python Script и Python Components.
  • Что делают start() и update().
  • Как получить доступ к текущему объекту из сценария.
  • Как получить компоненты из объекта.
  • Когда использовать Python Component или Python Code Component.

Цель состоит не в том, чтобы обучить всему языку Python. Цель состоит в том, чтобы сценарии Cave казались понятными, когда вы их открываете.

Где учить Python?

Если вы не знаете, как программировать на Python, Uniday Studio на самом деле предоставляет бесплатные обучающие курсы по Python. Перейдите на uniday.studio/learn, чтобы увидеть все варианты, или начните здесь:

В этом разделе мы предполагаем, что у вас уже есть понимание тем, рассматриваемых в этих двух учебных курсах.


Для чего используется Python в Cave

В Cave Python является слоем сценариев, который вы используете, когда игровому объекту нужно поведение.

Например, Python можно использовать для:

  • Перемещения игрока или врага.
  • Открытия двери.
  • Проигрывания анимации.
  • Вызова звука.
  • Запуска временной шкалы.
  • Смены сцен.
  • Обновления элемента UI.
  • Создания пользовательских инструментов редактора.

По сути, вы можете писать Python Scripts для создания всей логики вашей игры.

Именно поэтому стартовый проект уже содержит сценарии Python. Например, стандартный игрок не просто сетка с анимацией. У него также есть логика игрового процесса, которая считывает ввод, перемещает персонажа, вращает сетку и воспроизводит правильную анимацию.

Таким образом, когда вы пишете Python в Cave, вы обычно не пишете изолированный код. Вы пишете логику, которая управляет объектом и взаимодействует с компонентами, прикрепленными к этому объекту.

Ресурсы Скриптов и Компоненты Python

Код Python обычно находится внутри ресурса Python Script.

image.png

Затем, чтобы запустить этот скрипт в сцене, вы добавляете Python Component к объекту и выбираете, какой класс из скрипта должен выполняться.

Думайте об этом так:

Часть Что она делает
Ресурс Python Script Хранит код.
Python Component Запускает класс из этого скрипта на объекте.
Объект Объект, который контролируется скриптом.
Класс cave.Component Фактическое поведение, которое вы написали.

Например, у вас может быть ресурс сценария с названием Door Controller. Внутри него может быть класс с названием DoorController, который наследует от cave.Component. Затем вы добавляете Python Component к вашему объекту двери и выбираете этот класс.

Это разделение важно, потому что один и тот же скрипт можно переиспользовать. Вы можете разместить множество дверей в сцене, каждая из которых использует один и тот же скрипт контроллера двери, но с разными свойствами или разными дочерними объектами.

Минимальный Компонент Cave

Базовый компонент Python для Cave выглядит следующим образом:

import cave

class MyComponent(cave.Component):
    def start(self, scene):
        print("Компонент запущен!")

    def update(self):
        pass

Здесь есть несколько важных деталей:

  • import cave дает вашему сценарию доступ к API Python Cave.
  • class MyComponent(cave.Component) создает класс компонента, который может работать в Cave.
  • start(self, scene) запускается, когда компонент начинает свою работу.
  • update(self) выполняется каждый кадр, пока компонент активен.

Название MyComponent может быть любым, что вы хотите, но в реальных проектах следует использовать ясное имя, например, DoorController, EnemyAI, Checkpoint или PlayerHealth.

Методы Жизненного Цикла

Cave автоматически вызывает некоторые методы, когда ваш компонент работает.

Наиболее распространенные из них:

Метод Когда он запускается Обычное использование
start(self, scene) Когда компонент запускается. Получить ссылки, читать свойства, подготавливать переменные.
firstUpdate(self) Всегда после метода start каждого компонента объекта, в первом обновлении. Создать переменные, которые зависят от других компонентов, чтобы они были инициализированы.
update(self) Каждый кадр, если сцена не приостановлена. Движение, ввод, таймеры, проверки состояний.
pausedUpdate(self) Каждый кадр, если сцена приостановлена. Логика в паузе.
end(self, scene) Когда компонент заканчивает свою работу. Очистка, если это необходимо.

Большинство начальных сценариев используют start() и update(). Например, если вы создаете движущуюся платформу, start() — хорошее место для хранения исходной позиции, а update() — это место, где вы перемещаете платформу каждый кадр.

У нас также есть методы editorUpdate и lateUpdate, но мы не будем исследовать их здесь, так как они более продвинутые.

Доступ к Текущему Объекту

Внутри компонента Cave self.entity — это объект, которому принадлежит компонент Python.

Это одна из самых важных идей в скриптинге Cave. Сценарий не плавает вокруг сцены сам по себе. Он принадлежит объекту.

Вот простой пример:

import cave

class DoorController(cave.Component):
    def start(self, scene):
        self.transform = self.entity.getTransform()
        self.isOpen = False

    def update(self):
        pass

В этом сценарии:

  • self.entity — это объект двери.
  • self.entity.getTransform() получает компонент Transform двери.
  • self.isOpen — это переменная, используемая сценарием для запоминания, открыта ли дверь.

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

Получение Других Компонентов

Чтобы управлять объектом, вам обычно нужно получить один или несколько компонентов из него.

Например, сценарий игрока может получить:

  • Компонент Transform, чтобы перемещать или вращать объект.
  • Компонент Character, чтобы управлять движением персонажа.
  • Компонент Animation из дочернего объекта сетки, чтобы воспроизводить анимации.
  • Компонент Audio, чтобы воспроизвести зацикленный звук.

Вот небольшой пример:

import cave

class SimpleMover(cave.Component):
    def start(self, scene):
        self.transform = self.entity.getTransform()
        self.speed = 2.0

    def update(self):
        self.transform.move(0, 0, self.speed * cave.getDeltaTime(), local=True)

Это перемещает объект вперед каждый кадр.

Важная часть — это cave.getDeltaTime(). Поскольку update() выполняется каждый кадр, умножение движения на дельта-время поддерживает скорость движения постоянной, даже если частота кадров меняется.

Чтение Пользовательских Свойств

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

Например, вместо того чтобы писать это:

self.speed = 2.0

Вы можете считать значение из свойств объекта:

self.speed = self.entity.properties.get("speed", 2.0)

Это значит:

  • Если у объекта есть свойство speed, используйте его.
  • Если его нет, используйте 2.0 как значение по умолчанию.

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

Например:

Объект Свойство speed
Медленная Платформа 1.0
Быстрая Платформа 4.0
Движущаяся Опасность 7.0

Сценарий остается тем же, но поведение меняется для каждого объекта.

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

import cave

class PlatformMover(cave.Component):
    # Это будет локально:
    speed = 2.0

    def start(self, scene: cave.Scene):
        pass

    def update(self):
        events = cave.getEvents()

Переменная speed будет локально изменима для каждого экземпляра компонента:

image.png


Получение Дочерних Объектов

Многие объекты Cave построены как небольшие иерархии.

Шаблон игрока — хороший пример. Корневой объект Player имеет физику и логику персонажа, в то время как дочерний объект Mesh содержит визуальную сетку персонажа и компонент анимации.

Таким образом, если сценарий на корневом игроке хочет воспроизводить анимации, он сначала получает дочерний объект сетки:

import cave

class PlayerAnimationExample(cave.Component):
    def start(self, scene):
        self.mesh = self.entity.getChild("Mesh")
        self.animator = self.mesh.get("Animation")

    def update(self):
        self.animator.playByName("p-idle", blend=0.2, loop=True)

Это тот же шаблон, который используется в стандартном контроллере игрока:

  1. Получите дочерний объект.
  2. Получите компонент из этого дочернего объекта.
  3. Используйте компонент, когда это нужно.

Как только вы поймете этот шаблон, многие скрипты Cave становятся гораздо легче читать.

Метод getChild имеет необязательный параметр "рекурсивный", по умолчанию равный True. Если True, он будет запросить все дочерние объекты, включая дочерние дочерних объектов, пока не найдет запрашиваемый объект по имени.

Получение Объектов Сцены (Запросы Сцены)

При создании игры вам, скорее всего, нужно будет получить другие объекты в сцене. Поэтому давайте исследуем это. Первый шаг — получить саму сцену, и в Cave у вас есть два способа сделать это:

# Возвращает активную сцену:
scene = cave.getScene()

# Возвращает сцену, к которой принадлежит объект:
scene = self.entity.getScene()

Для удобства методы cave.Component start и end также принимают сцену в качестве параметра, так как вы, скорее всего, будете использовать их в этих методах. Для локального кода, такого как Python Code Component, также есть переменная scene, определенная по умолчанию, которую вы можете просто использовать, ожидая, что она сработает.

Как только вы получите сцену, вы можете получить конкретный объект по его имени, используя следующий код:

watchtower = scene.get("Watch Tower 01")

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

Всегда помните о том, чтобы проверять, вернул ли ваш запрос что-то действительное. Например:

# Запрос несуществующего объекта:
ent = scene.get("This Entity Doesnt Exist")

if ent is None:
    print("Недопустимый объект!")

Получение компонентов объекта

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

У объекта есть конкретный метод под названием get, в который вы можете передать имя компонента в виде строки, и он автоматически добавит объект, чтобы посмотреть, есть ли компонент, соответствующий вашему запросу:

animator = self.entity.get("Animation Component")

Чтобы упростить себе жизнь, если имя компонента заканчивается словом "Component", что довольно распространено, вы можете полностью опустить этот окончание при вводе имени компонента в этом методе. Вы также можете решить, хотите ли вы включить пробел в многословные имена компонентов или объединить все вместе.

Например, если вы хотите получить Rigid Body Component из объекта, хотя его имя в Python RigidBodyComponent, все приведенные ниже варианты будут работать:

rb = self.entity.get("Rigid Body")
rb = self.entity.get("RigidBody")
rb = self.entity.get("Rigid Body Component")
rb = self.entity.get("RigidBodyComponent")
rb = self.entity.get("RigidBody Component")

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

rb : cave.RigidBodyComponent = self.entity.get("Rigid Body")

Это позволяет IntelliSense работать в внешних редакторах, таких как Visual Studio Code, или даже встроенный автозаполнение, который предоставляется Cave Engine в его встроенном редакторе скриптов.

Для Transform Component, так как это один из самых распространенных типов компонентов, и вам придется часто его запрашивать, у объекта есть встроенный метод, чтобы получить его основной Transform:

transf = self.entity.getTransform()

Получение его с помощью вызова self.entity.getTransform() дает тот же результат, что получение его с помощью self.entity.get("Transform"), но первый вариант быстрее и более оптимизирован.

Если у объекта есть несколько компонентов с одним и тем же типом, он вернет первое совпадение, но иногда вам может понадобиться получить все совпадения. Например, в многоматериальном меше, который в Cave будет представлен объектом с несколькими компонентами меша, вы можете захотеть получить все компоненты меша. И для этого вы можете использовать метод getAll:

meshCmps = self.entity.getAll("Mesh")

# Изменение всех материалов на светящийся:
for meshCmp in meshCmps:
    meshCmp.material.setAsset("Glowing Material")

Получение Python-компонентов объекта

Теперь мы знаем, как получить нативные компоненты Cave, но что, если вы хотите получить компонент, который был написан вами с использованием Python, из объекта?

Для этого вам нужен специальный метод под названием getPy, который работает точно так же, как обычный метод get, за исключением того, что этот также будет возвращать Python-компоненты:

myCmp = self.entity.getPy("MyCustomComponent")

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

Как только вы получите ваш собственный Python-компонент, вы можете свободно получать доступ к его переменным и методам, созданным на Python:

myCmp = self.entity.getPy("MyCustomComponent")

# Изменение переменных Python:
myCmp.customValue = 10

# Вызов пользовательских методов:
myCmp.doSomething()
myCmp.applyDamage(10)

Python Code Component

Кроме обычного Python-компонента, Cave также имеет Python Code Component. Python Code Component полезен для быстрых скриптов, написанных непосредственно на объекте, без необходимости предварительно создавать отдельный ресурс Python Script.

У него есть все другие методы, а разница в том, что в компоненте Python вы пишете скрипт Python непосредственно внутри самого компонента, и он не является модульным и не может быть переиспользован. Так что если вы создаете копию объекта, это также создаст копию скрипта внутри него. Но иногда это может быть более быстрым способом создания некоторой простой логики, такой как вращающаяся монета.

Это хорошо для:

  • Быстрых тестов.
  • Небольших обратных вызовов.
  • Однократного поведения.
  • Прототипов.

Это не лучший вариант для более крупных игровых систем, потому что код труднее переиспользовать и организовать, но он оптимален для небольших кодов.

Python Component vs Python Code Component

Используйте это в качестве простого правила:

Используйте Это Когда
Python Component Поведение должно быть переиспользовано, отредактировано как ресурс или расти со временем.
Python Code Component Поведение небольшое, локальное.

Например, переиспользуемый EnemyAI должен быть ресурсом Python Script, используемым Python Component. Небольшой код, который что-то печатает или вызывает функцию, когда кнопка нажата, может быть Python Code Component.

Хорошая первая цель скрипта

Хороший первый скрипт в Cave — это что-то небольшое и видимое.

Попробуйте создать один из следующих:

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

Эти примеры небольшие, но они обучают наиболее важному рабочему процессу: получить объект, получить компонент, изменить что-то, протестировать в Play Mode и отрегулировать.

Что вы должны помнить

Скрипты Python в Cave обычно прикрепляются к объектам через Python Components.

Наиболее важный шаблон для начинающих:

  1. Используйте start(), чтобы получить ссылки и подготовить значения.
  2. Используйте update(), чтобы выполнять поведение каждый кадр.
  3. Используйте self.entity, чтобы получить доступ к объекту, который владеет скриптом.
  4. Используйте компоненты, чтобы фактически перемещать, анимировать, воспроизводить звук или управлять объектом.

Как только этот шаблон станет привычным, программирование в Cave станет гораздо менее таинственным. Вы не просто пишете код. Вы обучаете объекты, как себя вести.