Обработка коллизий

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

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

По умолчанию, SKNode объект не имеет никакой связанной организации физики, что означает, что это не принимает участие в моделировании физики. Мы используем это поведение по умолчанию для всех деревьев в Приключении, потому что деревья находятся в сцене просто для улучшения вида среды и не имеют прямого физического взаимодействия с символами.

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

Создание организаций физики

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

Каждая стена сконфигурирована с прямоугольной организацией физики, с dynamic набор свойств к NO.

  1. Adventure: APAAdventureScene.m -addCollisionWallAtWorldPoint:withWidth:height:
  2. - (void)addCollisionWallAtWorldPoint:(CGPoint)worldPoint withWidth:(CGFloat)width height:(CGFloat)height {
  3. ... (Create a rectangle with the specified size, and a basic node)
  4. wallNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:rect.size];
  5. wallNode.physicsBody.dynamic = NO;
  6. wallNode.physicsBody.categoryBitMask = APAColliderTypeWall;
  7. }
5

Мы устанавливаем dynamic к NO потому что позиция стены незатронута другими организациями физики — она не перемещается, даже если другие организации физики сталкиваются с нею.

Мы устанавливаем categoryBitMask свойство на каждой организации физики в сцене к одному из APAColliderType значения:

  1. Adventure: APACharacter.h
  2. typedef enum : uint8_t {
  3. APAColliderTypeHero = 1,
  4. APAColliderTypeGoblinOrBoss = 2,
  5. APAColliderTypeProjectile = 4,
  6. APAColliderTypeWall = 8,
  7. APAColliderTypeCave = 16
  8. } APAColliderType;

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

Все другие организации физики в игре принадлежат символам — мы создаем и конфигурируем их во время символьной инициализации.

Конфигурирование организаций физики

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

Каждый символ разделяет переопределения на подклассы configurePhysicsBody создать и сконфигурировать подходящую организацию физики для спрайта. APAHeroCharacter класс, например, создает круговую организацию и конфигурирует ее для столкновения со всем кроме снаряда.

  1. Adventure: APAHeroCharacter.m -configurePhysicsBody
  2. - (void)configurePhysicsBody {
  3. self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:kCharacterCollisionRadius];
  4. self.physicsBody.categoryBitMask = APAColliderTypeHero;
  5. self.physicsBody.collisionBitMask =
  6. APAColliderTypeGoblinOrBoss | APAColliderTypeHero |
  7. APAColliderTypeWall | APAColliderTypeCave;
  8. self.physicsBody.contactTestBitMask = APAColliderTypeGoblinOrBoss;
  9. }

Каждый из символьных подклассов установил a contactTestBitMask, что означает, что мы получаем обратный вызов каждый раз, когда организация физики вступает в контакт с другой организацией физики чей categoryBitMask установлен в один из указанных типов. Например, когда гоблин сталкивается со снарядом, мы хотим знать так, чтобы мы выполнили задачи, такие как применение ущерба или увеличение счета проигрывателя.

Ответ на коллизии

Обратные вызовы коллизии в Наборе Sprite являются ответственностью делегата физики; в Приключении это APAAdventureScene класс. Когда одна организация вступает в контакт с другим, didBeginContact: метод вызывают, который мы реализуем для проверки на символы включения коллизий и коллизии, включающие снаряды.

  1. Adventure: APAAdventureScene.m -didBeginContact:
  2. - (void)didBeginContact:(SKPhysicsContact *)contact {
  3. SKNode *node = contact.bodyA.node;
  4. if ([node isKindOfClass:[APACharacter class]]) {
  5. [(APACharacter *)node collidedWith:contact.bodyB];
  6. }
  7. ... (Repeat for bodyB)
  8. if (contact.bodyA.categoryBitMask & APAColliderTypeProjectile ||
  9. contact.bodyB.categoryBitMask & APAColliderTypeProjectile) {
  10. SKNode *projectile = ... (projectile is either bodyA.node or bodyB.node)
  11. [projectile runAction:[SKAction removeFromParent]];
  12. ... (Show the projectile sparks)
  13. }
  14. }
10

Если коллизия включает снаряд, снаряд достиг своего места назначения, и таким образом, мы удаляем его из сцены. Мы также выполняем эмиттер частицы с одним выстрелом для отображения искр в точке коллизии.

Любые побочные эффекты от коллизий, включающих один или несколько символов, обрабатываются символьными подклассами. Например, APAGoblin класс реализует collidedWith: если гоблин умирает, метод для контакта с хитами снаряда путем применения ущерба, запроса “получает пораженную” анимацию, и увеличение счета соответствующего проигрывателя.

  1. Adventure: APAGoblin.m -collidedWith:
  2. - (void)collidedWith:(SKPhysicsBody *)other {
  3. if (self.dying) {
  4. return;
  5. }
  6. if (other.categoryBitMask & APAColliderTypeProjectile) {
  7. self.requestedAnimation = APAAnimationStateGetHit;
  8. CGFloat damage = 100.0f;
  9. if ((arc4random_uniform(2)) == 0) {
  10. damage = 50.0f;
  11. }
  12. BOOL killed = [self applyDamage:damage fromProjectile:other.node];
  13. if (killed) {
  14. [[self characterScene] addToScore:10 afterEnemyKillWithProjectile:other.node];
  15. }
  16. }
  17. }
3

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

10

Мы используем arc4random_uniform() возвратить значение 0 или 1, который решает, должен ли снаряд причинить 50%-й или 100%-й ущерб.

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

applyDamage:fromProjectile: метод реализован APACharacter. Снаряды постепенно исчезают после путешествования на определенное расстояние, таким образом, мы хотим, чтобы потенция снаряда уменьшилась, поскольку это начинает исчезать. Мы используем applyDamage:fromProjectile: умножить предоставленное значение на снаряд alpha значение, прежде, чем вызвать applyDamage: метод для обработки уменьшения здоровья символа:

  1. Adventure: APACharacter.m -applyDamage:fromProjectile:
  2. - (BOOL)applyDamage:(CGFloat)damage fromProjectile:(SKNode *)projectile {
  3. return [self applyDamage:damage * projectile.alpha];
  4. }