Как сделать игру как Mega Jump With Sprite Kit: Часть 2/2

  1. Начиная
  2. Загрузка данных уровня
  3. Средний слой
  4. Parallaxalization
  5. Перемещение с акселерометром
  6. Система подсчета очков
  7. Строительство HUD
  8. Награждение очков
  9. Игра закончена!
  10. Перезапуск
  11. Куда пойти отсюда?

Добро пожаловать во вторую часть серии уроков, в которой вы узнаете, как использовать Sprite Kit для создания игры, подобной Mega Jump .

в первая часть В этом уроке вы создали новую игру Sprite Kit под названием «Uber Jump». Вы добавили графику, спрайт игрока и некоторые элементы игрового процесса.

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

Как с Первая часть убедитесь, что вы знакомы с основы Sprite Kit прежде чем продолжить.

Ваш уровень ждет - так что давайте перейдем к нему!

Начиная

Если у вас его еще нет, захватить копию полного проекта из первой части.

Ваш уровень будет содержать много звезд и платформ. Вместо того, чтобы расположить их вручную, загрузите это файл конфигурации уровня. Разархивируйте его и перетащите Level01.plist в ваш проект Xcode. Убедитесь, что флажок Копировать элементы в папку целевой группы (если необходимо) установлен и выбран целевой объект UberJump .

Откройте Level01.plist и проверьте его содержимое. В корне у него есть три элемента:

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

Элементы « Звезды» и « Платформы» содержат два подэлемента:

  • Шаблоны содержат множество многократно используемых шаблонов звезд или платформ.
  • Positions указывает, где размещать узоры звезд или платформ по всему уровню.

Positions указывает, где размещать узоры звезд или платформ по всему уровню

Чтобы лучше понять формат файла, взгляните на Звезды / Позиции / Элемент 0 . Он содержит три элемента, говорящих игре о размещении звезд в виде крестов, расположенных в точке (160, 240)

Он содержит три элемента, говорящих игре о размещении звезд в виде крестов, расположенных в точке (160, 240)

Теперь посмотрите на Patterns / Cross и вы увидите, что этот шаблон состоит из пяти элементов, включая (x, y) координаты относительно позиции, указанной в Stars / Positions и типе звезды, где NORMAL = 0 или SPECIAL = 1 ,

Теперь посмотрите на Patterns / Cross и вы увидите, что этот шаблон состоит из пяти элементов, включая (x, y) координаты относительно позиции, указанной в Stars / Positions и типе звезды, где NORMAL = 0 или SPECIAL = 1 ,

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

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

Загрузка данных уровня

Чтобы добавить поддержку загрузки уровня из Level01.plist , откройте MyScene.m и добавьте следующую переменную экземпляра в расширение класса в MyScene.m :

// Высота, на которой заканчивается уровень int _endLevelY;

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

Вставьте следующий код в initWithSize: прямо перед строками, которые создают и добавляют платформу:

// Загружаем уровень NSString * levelPlist = [[NSBundle mainBundle] pathForResource: @ "Level01" ofType: @ "plist"]; NSDictionary * levelData = [NSDictionary dictionaryWithContentsOfFile: levelPlist]; // Высота, на которой игрок заканчивает уровень _endLevelY = [levelData [@ "EndY"] intValue];

Это загружает данные из списка свойств в словарь с именем levelData и сохраняет значение EndY списка свойств в _endLevelY.

Теперь о звездах и платформах. Начните с платформ. В initWithSize: замените следующие строки:

// Добавить платформу PlatformNode * platform = [self createPlatformAtPosition: CGPointMake (160, 320) ofType: PLATFORM_NORMAL]; [_foregroundNode addChild: платформа];

С этим кодом:

// Добавить платформы NSDictionary * platform = levelData [@ "Platforms"]; NSDictionary * platformPatterns = platform [@ "Patterns"]; NSArray * platformPositions = platform [@ "Positions"]; for (NSDictionary * platformPosition в platformPositions) {CGFloat patternX = [platformPosition [@ "x"] floatValue]; CGFloat patternY = [platformPosition [@ "y"] floatValue]; NSString * pattern = platformPosition [@ "pattern"]; // Поиск шаблона NSArray * platformPattern = platformPatterns [pattern]; для (NSDictionary * platformPoint в platformPattern) {CGFloat x = [platformPoint [@ "x"] floatValue]; CGFloat y = [platformPoint [@ "y"] floatValue]; PlatformType type = [platformPoint [@ "type"] intValue]; PlatformNode * platformNode = [self createPlatformAtPosition: CGPointMake (x + patternX, y + patternY) ofType: type]; [_foregroundNode addChild: platformNode]; }}

Здесь много чего происходит, но все просто. Вы загружаете словарь платформ из levelData и затем просматриваете его массив Positions. Для каждого элемента в массиве вы загружаете соответствующий шаблон и создаете экземпляры PlatformNodes правильного типа в указанных (x, y) позициях. Вы добавляете все узлы платформы на узел переднего плана, к которому принадлежат все игровые объекты.

Сборка и запуск. Вы увидите набор из трех платформ, выровненных по сцене, который представляет собой шаблон «Triple», описанный в Level01.plist .

plist

Теперь сделайте то же самое для звезд. Внутри MyScene.m замените следующую строку в initWithSize ::

// Добавить звезду StarNode * star = [self createStarAtPosition: CGPointMake (160, 220) ofType: STAR_SPECIAL]; [_foregroundNode addChild: star];

С этим кодом:

// Добавить звезды NSDictionary * stars = levelData [@ "Stars"]; NSDictionary * starPatterns = stars [@ "Patterns"]; NSArray * starPositions = stars [@ "Positions"]; for (NSDictionary * starPosition в starPositions) {CGFloat patternX = [starPosition [@ "x"] floatValue]; CGFloat patternY = [starPosition [@ "y"] floatValue]; NSString * pattern = starPosition [@ "pattern"]; // Искать шаблон NSArray * starPattern = starPatterns [pattern]; для (NSDictionary * starPoint в starPattern) {CGFloat x = [starPoint [@ "x"] floatValue]; CGFloat y = [starPoint [@ "y"] floatValue]; StarType type = [starPoint [@ "type"] intValue]; StarNode * starNode = [self createStarAtPosition: CGPointMake (x + patternX, y + patternY) ofType: type]; [_foregroundNode addChild: starNode]; }}

Это именно то, что вы сделали для создания платформ, но на этот раз вы создаете звезды для элементов в словаре звезд.

Сборка и запуск. Это начинает выглядеть как настоящая игра!

Средний слой

Графически, есть еще одна вещь, которую нужно добавить, чтобы придать игре большую иллюзию глубины, и это средний слой. Это узел, который будет содержать декоративную графику, чтобы оживить игру.

Добавьте следующий метод в MyScene.m :

- (SKNode *) createMidgroundNode {// Создать узел SKNode * midgroundNode = [узел SKNode]; // 1 // Добавляем несколько веток в середину для (int i = 0; i <10; i ++) {NSString * spriteName; // 2 int r = arc4random ()% 2; if (r> 0) {spriteName = @ "BranchRight"; } else {spriteName = @ "BranchLeft"; } // 3 SKSpriteNode * branchNode = [SKSpriteNode spriteNodeWithImageNamed: spriteName]; branchNode.position = CGPointMake (160.0f, 500.0f * i); [midgroundNode addChild: branchNode]; } // Возвращаем законченный фоновый узел return midgroundNode; }

Внимательно посмотрите на этот код:

  1. Вы добавляете десять ветвей в midgroundNode, равномерно распределенных по всему уровню.
  2. Есть два разных изображения ветвей, одно из которых показывает ответвления слева от экрана, а другое справа. Здесь вы берете один случайно.
  3. Вы размещаете ветви с интервалами в 500 точек на оси Y среднего узла.

Теперь добавьте узел midground к вашей сцене, вставив следующий код в initWithSize:, сразу после строки, которая добавляет фоновый узел:

// Midground _midgroundNode = [self createMidgroundNode]; [self addChild: _midgroundNode];

Сборка и запуск. Смотри! Это ветка (своего рода) и, возможно, несколько розовых бабочек!

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

Нажмите, чтобы начать игру, и вы увидите, как спрайт игрока выстрелил вверх по экрану. Однако, даже когда Uber Jumper поднимается, игровой мир остается неподвижным.

Однако, даже когда Uber Jumper поднимается, игровой мир остается неподвижным

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

Parallaxalization

Нет, это не слово! ;]

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

Откройте MyScene.m и добавьте следующий метод:

- (void) update: (CFTimeInterval) currentTime {// Рассчитать смещение игрока y, если (_player.position.y> 200.0f) {_backgroundNode.position = CGPointMake (0.0f, - ((_ player.position.y - 200.0f) / 10)); _midgroundNode.position = CGPointMake (0.0f, - ((_ player.position.y - 200.0f) / 4)); _foregroundNode.position = CGPointMake (0.0f, - (_ player.position.y - 200.0f)); }}

Вы проверяете, чтобы убедиться, что узел игрока переместился вверх на экран как минимум на 200 пунктов, потому что иначе вы не хотите перемещать фон. Если это так, вы перемещаете три узла вниз на разных скоростях, чтобы получить эффект параллакса:

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

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

Отличная работа! Но пока не почивайте на лаврах. Чтобы подняться выше в этом мире, вам нужно включить свой наклон.

Перемещение с акселерометром

Пришло время рассмотреть акселерометр. У вас хорошо работает движение по вертикальной оси, но как насчет движения по горизонтальной оси? Как и в Mega Jump , игрок будет управлять Uber Jumper с помощью акселерометра.

Примечание. Для тестирования акселерометра вам необходимо запустить игру на устройстве. Симулятор iPhone не имитирует входы акселерометра.

Входы акселерометра являются частью библиотеки Core Motion, поэтому в верхней части MyScene.m добавьте следующий импорт:

@import CoreMotion;

Затем добавьте следующие строки в расширение класса MyScene:

// Менеджер движения для акселерометра CMMotionManager * _motionManager; // Значение ускорения от акселерометра CGFloat _xAcceleration;

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

Чтобы создать экземпляр CMMotionManager, добавьте следующий код в initWithSize: сразу после строки, которая добавляет _tapToStartNode в HUD:

// CoreMotion _motionManager = [[CMMotionManager alloc] init]; // 1 _motionManager.accelerometerUpdateInterval = 0.2; // 2 [_motionManager startAccelerometerUpdatesToQueue: [NSOperationQueue currentQueue] withHandler: ^ (CMAccelerometerData * accelerometerData, NSError * error) {// 3 CMAcceleration acceleration = accelerometerData.acceleleration; // 4 _xAcceleration = (acceleration.x * 0,75) + (_xAcceleration * 0,25); }];

Здесь много чего происходит, поэтому сделайте более глубокое погружение:

  1. accelerometerUpdateInterval определяет количество секунд между обновлениями акселерометра. Значение 0,2 обеспечивает плавную частоту обновления для изменений акселерометра.
  2. Вы включаете акселерометр и предоставляете блок кода для выполнения при каждом обновлении акселерометра.
  3. Внутри блока вы получаете информацию об ускорении из последних данных акселерометра, переданных в блок.
  4. Здесь вы рассчитываете ускорение оси игрока. Вы можете использовать значение x непосредственно из данных акселерометра, но вы получите намного более плавное движение, используя значение, полученное из трех четвертей ускорения оси x акселерометра (скажем, в три раза быстрее!) И одной четверти текущего x осевое ускорение.

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

Так как вы напрямую управляете скоростью узла игрока, важно, чтобы Sprite Kit сначала обработал физику. Sprite Kit предоставляет метод для этой цели, который называется didSimulatePhysics. Вы должны назвать это, как только Sprite Kit разберется с физикой игры для каждого кадра.

Добавьте следующий метод в MyScene.m :

- (void) didSimulatePhysics {// 1 // Установить скорость на основе ускорения по оси x _player.physicsBody.velocity = CGVectorMake (_xAcceleration * 400.0f, _player.physicsBody.velocity.dy); // 2 // Проверка границ x if (_player.position.x <-20.0f) {_player.position = CGPointMake (340.0f, _player.position.y); } else if (_player.position.x> 340.0f) {_player.position = CGPointMake (-20.0f, _player.position.y); } возвращение; }

Здесь происходит пара вещей:

  1. Вы изменяете часть оси X скорости узла игрока, используя значение _xAcceleration. Вы умножаете его на 400,0, потому что шкала акселерометра не соответствует шкале мира физики и, следовательно, ее увеличение дает более удовлетворительный результат. Вы оставляете значение оси Y в одиночку, потому что акселерометр не влияет на него.
  2. В Mega Jump , когда игрок покидает экран слева или справа, он возвращается на экран с другой стороны. Вы повторяете это поведение здесь, проверяя границы экрана и оставляя 20-точечную границу по краям.

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

Система подсчета очков

Ваш полный Uber Jump будет иметь три части информации, относящейся к игроку:

  • Текущий счет. Каждая игра, счет начнется с нуля. Чем выше игрок получает, тем больше очков вы начисляете на счет. Вы также будете добавлять очки за каждую звезду, которую набирает игрок.
  • Высокий балл. Каждый проход по игре приведет к окончательному счету. Uber Jump сохранит самое высокое из них в пользовательских настройках приложения по умолчанию, чтобы игрок всегда знал счет, который нужно побить.
  • Звезды. В то время как текущий счет будет сбрасываться в начале каждой игры, звезды игрока будут накапливаться от игры к игре. В будущей версии Uber Jump вы можете решить сделать из звезд игровую валюту, с помощью которой пользователи могут покупать обновления и улучшения. Вы не будете делать это как часть этого урока, но у вас будет валюта на случай, если вы захотите сделать это самостоятельно.

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

Создайте новый класс Objective C под названием GameState и сделайте его подклассом NSObject .

Создайте новый класс Objective C под названием GameState и сделайте его подклассом NSObject

Добавьте следующие свойства и метод в GameState.h :

@property (nonatomic, assign) int score; @property (nonatomic, assign) int highScore; @property (nonatomic, assign) int stars; + (instancetype) sharedInstance;

Эти три свойства обеспечат доступ к текущей оценке, высокой оценке и счету звезд. Статический метод sharedInstance предоставит доступ к одноэлементному экземпляру GameState.

Теперь добавьте следующее к GameState.m :

+ (instancetype) sharedInstance {статическое dispatch_once_t pred = 0; static GameState * _sharedInstance = nil; dispatch_once (& pred, ^ {_sharedInstance = [[super alloc] init];}); вернуть _sharedInstance; }

Это стандартный способ создания одноэлементного объекта в Objective-C. Это гарантирует, что GameState, на который ссылается _sharedInstance, инициализируется только один раз, независимо от того, сколько раз вы вызываете этот метод. Теперь вы можете использовать один и тот же объект в любом месте вашего кода для доступа к текущему состоянию игры.

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

Добавьте следующий метод init в GameState.m :

- (id) init {if (self = [super init]) {// Init _score = 0; _highScore = 0; _stars = 0; // Загружаем игровое состояние NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; id highScore = [значения по умолчанию objectForKey: @ "highScore"]; if (highScore) {_highScore = [highScore intValue]; } id stars = [значения по умолчанию objectForKey: @ "stars"]; if (stars) {_stars = [stars intValue]; }} вернуть себя; }

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

Примечание. На следующей неделе появится полное руководство под названием «Как хранить данные вашей игры». Он познакомит вас с использованием связки ключей, iCloud и других инструментов для хранения ваших рекордов и других игровых данных.

Чтобы сохранить эти значения, вам нужен метод в GameState. Добавьте следующее объявление метода в GameState.h :

- (void) saveState;

Теперь добавьте реализацию в GameState.m :

- (void) saveState {// Обновление highScore, если текущий результат больше _highScore = MAX (_score, _highScore); // Сохраняем пользовательские настройки по умолчанию NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; [значения по умолчанию setObject: [NSNumber numberWithInt: _highScore] forKey: @ "highScore"]; [значения по умолчанию setObject: [NSNumber numberWithInt: _stars] forKey: @ "stars"]; [[NSUserDefaults standardUserDefaults] синхронизировать]; }

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

Найдите и откройте UberJump-Prefix.pch в Навигаторе проекта. Добавьте следующий импорт после импорта для заголовочных файлов UIKit и Foundation:

#import "GameState.h"

Это дает каждому вашему классу доступ к GameState без непосредственного импорта его заголовочного файла.

Что хорошего в счете, если никто не знает, что это такое? Ты должен показать мне деньги!

Строительство HUD

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

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

Добавьте следующие объявления переменных в расширение класса в MyScene.m :

// Ярлыки для очков и звезд SKLabelNode * _lblScore; SKLabelNode * _lblStars;

Чтобы построить HUD, добавьте следующий код в initWithSize: в MyScene.m непосредственно перед строкой, которая инициализирует _motionManager:

// Создание HUD // Звезды // 1 SKSpriteNode * star = [SKSpriteNode spriteNodeWithImageNamed: @ "Star"]; star.position = CGPointMake (25, self.size.height-30); [_hudNode addChild: star]; // 2 _lblStars = [SKLabelNode labelNodeWithFontNamed: @ "ChalkboardSE-Bold"]; _lblStars.fontSize = 30; _lblStars.fontColor = [SKColor whiteColor]; _lblStars.position = CGPointMake (50, self.size.height-40); _lblStars.horizontAlignmentMode = SKLabelHor horizontalAlignmentModeLeft; // 3 [_lblStars setText: [NSString stringWithFormat: @ "X% d", [GameState sharedInstance] .stars]]; [_hudNode addChild: _lblStars]; // Score // 4 _lblScore = [SKLabelNode labelNodeWithFontNamed: @ "ChalkboardSE-Bold"]; _lblScore.fontSize = 30; _lblScore.fontColor = [SKColor whiteColor]; _lblScore.position = CGPointMake (self.size.width-20, self.size.height-40); _lblScore.horizontAlignmentMode = SKLabelHor horizontalAlignmentModeRight; // 5 [_lblScore setText: @ "0"]; [_hudNode addChild: _lblScore];

Внимательно посмотрите на этот раздел кода:

  1. Сначала вы добавляете звездочку в верхнем левом углу сцены, чтобы сообщить игроку, что следующий номер - это количество звездочек.
  2. Рядом со звездой вы размещаете выровненный по левому краю SKLabelNode.
  3. Вы инициализируете метку количеством звездочек из GameState.
  4. Вы добавляете выровненный по правому краю SKLabelNode в верхнем правом углу сцены.
  5. Вы инициализируете этот ярлык на ноль, так как в настоящее время нет оценки.

Сборка и запуск. Вы увидите две метки в верхней части экрана.

Последний слой вашей игры завершен! Теперь давайте уже наберем немного очков!

Награждение очков

Наконец пришло время начислять очки за тяжелую работу игрока. В Uber Jump есть два способа набрать очки: взбираться по сцене и собирать звезды.

Чтобы присуждать баллы за сбор звезд, просто добавьте следующий код в конец collisionWithPlayer: в StarNode.m , непосредственно перед оператором return:

// Награда [GameState sharedInstance] .score + = (_starType == STAR_NORMAL? 20: 100);

Это оно! Вы добавляете 20 баллов к баллу за нормальную звезду и 100 баллов за специальный тип.

Чтобы показать этот обновленный счет, вернитесь к didBeginContact: в MyScene.m . Вспомните из первой части этого руководства, что этот метод устанавливает флаг с именем updateHUD в значение YES, когда он определяет, что значения, отображаемые в HUD, необходимо изменить.

Добавьте следующие две строки кода в didBeginContact: внутри фигурных скобок условия if (updateHUD) {}. Там должен быть комментарий, который гласит: TODO: обновление HUD в части 2:

[_lblStars setText: [NSString stringWithFormat: @ "X% d", [GameState sharedInstance] .stars]]; [_lblScore setText: [NSString stringWithFormat: @ "% d", [GameState sharedInstance] .score]];

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

По мере того, как вы собираете звезды, следите за увеличением ваших очков

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

Добавьте следующую переменную экземпляра в расширение класса в MyScene.m :

// Max y достигнут игроком int _maxPlayerY;

Игровой узел изначально начинается с y-координаты 80.0, поэтому вам нужно инициализировать _maxPlayerY равным 80, если вы не хотите давать игроку 80 очков только за начало игры. ;]

Добавьте следующую строку в initWithSize: в MyScene.m , сразу после строки, которая устанавливает цвет фона:

// Сброс _maxPlayerY = 80;

Чтобы увеличить счет, когда игрок перемещается вверх по экрану, перейдите на MyScene.m . В верхней части обновления: добавьте следующие строки:

// Новая максимальная высота? // 1 if ((int) _player.position.y> _maxPlayerY) {// 2 [GameState sharedInstance] .score + = (int) _player.position.y - _maxPlayerY; // 3 _maxPlayerY = (int) _player.position.y; // 4 [_lblScore setText: [NSString stringWithFormat: @ "% d", [GameState sharedInstance] .score]]; }

Это заслуживает более тщательного изучения:

  1. Во-первых, вы проверяете, прошел ли узел игрока выше, чем он прошел в этом проходе.
  2. Если это так, вы добавляете к счету разницу между текущей y-координатой узла игрока и максимальным y-значением.
  3. Вы устанавливаете новое максимальное значение y.
  4. Наконец, вы обновляете метку счета с новым счетом.

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

Теперь рассмотрим количество звезд. Вам нужно увеличивать его каждый раз, когда узел игрока сталкивается со звездой, поэтому откройте StarNode.m и добавьте следующий код в collisionWithPlayer: сразу после строки, которая присуждает очки:

// Награждение звезд [GameState sharedInstance] .stars + = (_starType == STAR_NORMAL? 1: 5);

Это все, что нужно сделать! Постройте и запустите, и нажмите, чтобы начать. Наблюдайте за увеличением количества звезд по мере их сбора.

Во время игры вы можете заметить, что при падении все пройденные вами игровые объекты все еще находятся в игре. «Привет!» - вы наверняка думаете: «Я провел значительное количество времени с Mega Jump, и я уверен, что это не так, как было в этой игре!» Да, но это легко исправить.

Напомним, что вы добавили метод с именем checkForNodeRemoval: в GameObjectNode, который проверяет, нужно ли удалять узел. Теперь пришло время вызывать этот метод каждый кадр.

Добавьте следующий код для обновления: в MyScene.m перед строкой, которая проверяет, больше ли позиция узла игрока больше 200:

// Удаляем игровые объекты, которые прошли мимо [_foregroundNode enumerateChildNodesWithName: @ "NODE_PLATFORM" usingBlock: ^ (узел SKNode *, BOOL * stop) {[((PlatformNode *) узел) checkNodeRemoval: _player.position.y]; }]; [_foregroundNode enumerateChildNodesWithName: @ "NODE_STAR" usingBlock: ^ (узел SKNode *, BOOL * stop) {[((StarNode *) узел) checkNodeRemoval: _player.position.y]; }];

Здесь вы перечисляете через платформы в узле переднего плана и вызываете checkNodeRemoval: для каждого. Затем вы делаете то же самое для каждой звезды.

Постройте и запустите, и нажмите, чтобы начать. Теперь, когда ты падаешь, это не что иное, как пустое небо!

Теперь, когда ты падаешь, это не что иное, как пустое небо

Игра закончена!

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

Создайте новый класс Objective C под названием EndGameScene и сделайте его подклассом SKScene .

Создайте новый класс Objective C под названием EndGameScene и сделайте его подклассом SKScene

EndGameScene будет простым экраном, который показывает счет игрока, его количество звезд и текущий высокий балл. Вы добавляете все узлы в initWithSize :, поэтому откройте EndGameScene.m и добавьте следующий метод:

- (id) initWithSize: (CGSize) size {if (self = [super initWithSize: size]) {// Звезды SKSpriteNode * star = [SKSpriteNode spriteNodeWithImageNamed: @ "Star"]; star.position = CGPointMake (25, self.size.height-30); [self addChild: star]; SKLabelNode * lblStars = [SKLabelNode labelNodeWithFontNamed: @ "ChalkboardSE-Bold"]; lblStars.fontSize = 30; lblStars.fontColor = [SKColor whiteColor]; lblStars.position = CGPointMake (50, self.size.height-40); lblStars.horizontAlignmentMode = SKLabelHor horizontalAlignmentModeLeft; [lblStars setText: [NSString stringWithFormat: @ "X% d", [GameState sharedInstance] .stars]]; [self addChild: lblStars]; // Score SKLabelNode * lblScore = [SKLabelNode labelNodeWithFontNamed: @ "ChalkboardSE-Bold"]; lblScore.fontSize = 60; lblScore.fontColor = [SKColor whiteColor]; lblScore.position = CGPointMake (160, 300); lblScore.horizontAlignmentMode = SKLabelHor horizontalAlignmentModeCenter; [lblScore setText: [NSString stringWithFormat: @ "% d", [GameState sharedInstance] .score]]; [self addChild: lblScore]; // высокий балл SKLabelNode * lblHighScore = [SKLabelNode labelNodeWithFontNamed: @ "ChalkboardSE-Bold"]; lblHighScore.fontSize = 30; lblHighScore.fontColor = [SKColor cyanColor]; lblHighScore.position = CGPointMake (160, 150); lblHighScore.horizontAlignmentMode = SKLabelHor HorizontalAlignmentModeCenter; [lblHighScore setText: [NSString stringWithFormat: @ "High Score:% d", [GameState sharedInstance] .highScore]]; [self addChild: lblHighScore]; // Попробуйте еще раз SKLabelNode * lblTryAgain = [SKLabelNode labelNodeWithFontNamed: @ "ChalkboardSE-Bold"]; lblTryAgain.fontSize = 30; lblTryAgain.fontColor = [SKColor whiteColor]; lblTryAgain.position = CGPointMake (160, 50); lblTryAgain.horizontAlignmentMode = SKLabelHor HorizontalAlignmentModeCenter; [lblTryAgain setText: @ "Нажмите, чтобы попробовать снова"]; [self addChild: lblTryAgain]; } вернуть себя; }

Это много кода, но теперь, после всего, что вы сделали в этом уроке, вы получили pwnage.

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

Чтобы отследить, закончилась ли игра, добавьте следующую BOOL в расширение класса в MyScene.m :

// Игра окончена, чувак! BOOL _gameOver;

В конце игры синглтон GameState должен сохранить текущее состояние и перейти на новую сцену. В MyScene.m добавьте следующий импорт вверху:

#import "EndGameScene.h"

Теперь добавьте следующий метод в MyScene.m :

- (void) endGame {// 1 _gameOver = YES; // 2 // Сохранить звезды и рекорд [[GameState sharedInstance] saveState]; // 3 SKScene * endGameScene = [[EndGameScene alloc] initWithSize: self.size]; SKTransition * show = [SKTransition fadeWithDuration: 0,5]; [self.view presentScene: endGameScene transition: открыть]; }

Посмотрите на этот метод подробно:

  1. Сначала вы установите для _gameOver значение YES.
  2. Затем вы указываете синглтону GameState сохранять состояние игры в соответствии с пользовательскими настройками приложения.
  3. Наконец, вы создаете экземпляр EndGameScene и переходите к нему, замирая в течение 0,5 секунды.

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

Откройте MyScene.m и добавьте следующий код в конец обновления:

// 1 // Проверяем, закончили ли мы уровень if (_player.position.y> _endLevelY) {[self endGame]; } // 2 // Проверьте, не зашли ли мы слишком далеко, если (_player.position.y <(_maxPlayerY - 400)) {[self endGame]; }

Посмотрите на эти проверки:

  1. Помните, вы загрузили _endLevelY из списка свойств уровня - это значение y, при котором игрок закончил уровень.
  2. Если узел игрока падает более чем на 400 пунктов ниже максимальной высоты, которую он достиг, то игра окончена.

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

Добавьте следующую строку в начало обновления: в MyScene.m :

if (_gameOver) return;

Теперь, когда игра вызывает update:, метод проверяет, завершена ли игра, прежде чем продолжить.

Сборка и запуск. Нажмите, чтобы начать, а затем играть в игру, позволяя Uber Jumper в какой-то момент упасть. Игра перейдет в конец игровой сцены.

Перезапуск

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

Откройте EndGameScene.m и добавьте следующий импорт вверху:

#import "MyScene.h"

Теперь добавьте следующий метод для обработки сенсорных событий:

- (void) touchesBegan: (NSSet *) затрагивает событие Event: (UIEvent *) {// Переход обратно в игру SKScene * myScene = [[MyScene alloc] initWithSize: self.size]; SKTransition * show = [SKTransition fadeWithDuration: 0,5]; [self.view presentScene: переход myScene: раскрыть]; }

Этот код просто переходит на новую MyScene во многом так же, как вы переходите на эту сцену при первом запуске приложения.

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

Ой, вы забыли сбросить счет! Исправьте это сейчас, остановив приложение и перейдя на MyScene.m . В initWithSize :, где вы сбрасываете _maxPlayerY, добавьте следующий код:

[GameState sharedInstance] .score = 0; _gameOver = NO;

Это сбрасывает счет в синглтоне GameState, а также сбрасывает флаг _gameOver.

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

На этот раз счет сбрасывается до нуля

А теперь иди и побей этот высокий балл! ;]

Куда пойти отсюда?

Поздравляю, вы сделали такую ​​игру, как Mega Jump !

Вот это пример проекта со всем кодом из этой серии руководств.

В процессе разработки игры на основе физики. Вы узнали много нового о том, как настраивать коллизии и как выстраивать свою игровую логику, обнаруживая контакты в игре.

Но вы можете сделать гораздо больше для своей игры Uber Jump! Откройте данные уровня plist и подумайте о новых уровнях. Какие новые формы вы могли бы реализовать? Вы можете просто скопировать исходный код plist и создать любое количество уровней, изменяя содержимое файла уровней.

Сыграйте в Mega Jump и получите вдохновение! Можете ли вы реализовать какой-то лучший игровой подвиг по сравнению с оригинальной игрой?

И последнее, но не менее важное: если вы хотите узнать больше о Sprite Kit, ознакомьтесь с Игры для iOS от Tutorials книга, где вы узнаете, как сделать пять полных игр - от действия зомби до гонок на автомобилях и перестрелок в космосе!

А пока, если у вас есть какие-либо вопросы или комментарии, присоединяйтесь к обсуждению на форуме ниже.

У вас хорошо работает движение по вертикальной оси, но как насчет движения по горизонтальной оси?
Что хорошего в счете, если никто не знает, что это такое?
StarType == STAR_NORMAL?
StarType == STAR_NORMAL?
Куда пойти отсюда?
Какие новые формы вы могли бы реализовать?
Можете ли вы реализовать какой-то лучший игровой подвиг по сравнению с оригинальной игрой?