Ну вот мы и подошли к последней, заключительной части нашего урока. В предыдущих частях: части 1, части 2, части 3, мы создали физику игры, подключили графическую оболочку и научили нашего героя реагировать на наклон устройства. Осталось совсем немного – сделать подсчет очков и дописать кое-какие мелочи по коду. В результате у нас получится настоящая игра, готовая для размещения в App Store…
Создание HUD
Перед тем как начать считать очки, мы должны обеспечить их отображение на экране HUD, чтобы игрок мог видеть набранные баллы и количество звезд.
В левом верхнем углу сцены на нашем HUD будет отображаться общее число собранных звезд, а текущий счет – в правом верхнем углу. Для этого нам нужно создать два лейбла класса SKLabelNodes.
Создайте следующие свойства в файле GameScene.swift:
// Labels for score and stars var lblScore: SKLabelNode! var lblStars: SKLabelNode! |
Чтобы скомпоновать HUD, добавьте следующий код в метод init(size:) класса GameScene.swift сразу перед строкой, которая инициализирует MotionManager:
// Build the HUD // Stars // 1 let star = SKSpriteNode(imageNamed: "Star") star.position = CGPoint(x: 25, y: self.size.height-30) hudNode.addChild(star) // 2 lblStars = SKLabelNode(fontNamed: "ChalkboardSE-Bold") lblStars.fontSize = 30 lblStars.fontColor = SKColor.whiteColor() lblStars.position = CGPoint(x: 50, y: self.size.height-40) lblStars.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Left // 3 lblStars.text = String(format: "X %d", GameState.sharedInstance.stars) hudNode.addChild(lblStars) // Score // 4 lblScore = SKLabelNode(fontNamed: "ChalkboardSE-Bold") lblScore.fontSize = 30 lblScore.fontColor = SKColor.whiteColor() lblScore.position = CGPoint(x: self.size.width-20, y: self.size.height-40) lblScore.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Right // 5 lblScore.text = "0" hudNode.addChild(lblScore) |
Рассмотрим детально этот блок кода:
1. Сначала мы добавили графику для звезды в левом верхнем углу сцены, чтобы показать игроку, что стоящее рядом с ней число — это количество собранных звезд.
2. Рядом со звездой, мы размещаем SKLabelNode, в котором текст выровнен по левому краю.
3. Мы отобразили на лейбле количество звезд, взятого из класса GameState.
4. Мы добавили в правый верхний угол сцены лейбл SKLabelNode с текстом, выровненным по правому краю.
5. Мы отобразили на этом лейбле ноль, так как в настоящее время никакого счета ещё нет.
Постройте и запустите. Вы увидите два лейбла в верхней части экрана.
Начисление очков
Наконец пришло время, чтобы начать начислять игроку очки за его тяжкий труд. В Uber Jump набрать очки можно двумя способами: подниматься вверх по сцене и собирать звезды.
Для того, чтобы настроить получение очков за звезды, откройте GameObjectNode.swift и просто добавьте следующий код в нижнюю часть метода collisionWithPlayer класса StarNode сразу перед оператором return:
// Award score GameState.sharedInstance.score! += (starType == .Normal ? 20 : 100) |
И это всё! Мы добавляем 20 очков к счету за обычную звезду и 100 очков за звезду специального типа.
Чтобы отобразить обновленный счет, давайте вернёмся к методу didBeginContact в GameScene.swift. Напомним, что в первой части этого урока в этом методе мы устанавливали значение true для флага с именем updateHUD в случае необходимости изменять отображаемые в HUD значения.
Заключите следующие две строчки кода между фигурных скобок условия if updateHUD {…} внутри этого метода (сейчас там должен быть комментарий с текстом «TODO: Update HUD in Part 2»):
lblStars.text = String(format: "X %d", GameState.sharedInstance.stars) lblScore.text = String(format: "%d", GameState.sharedInstance.score) |
Постройте и запустите. Нажмите, чтобы начать игру. По мере того как вы будете собирать звезды, ваш счет станет увеличиваться.
Чтобы понимать, когда именно нужно начислять очки за путешествие вверх по экрану, вы должны хранить самую высокую точку по оси Y, которой достиг герой в течение текущей игры. Мы будем использовать эти данные, чтобы увеличивать счет только тогда, когда герой добирается до новой наивысшей точки, а не присуждать очки постоянно, пока игрок прыгает вверх и вниз по экрану.
Добавьте следующее свойство класса в файл GameScene.swift:
// Max y reached by player var maxPlayerY: Int |
Узел героя начинает движение, когда его координата по оси Y равна 80, поэтому вы должны присвоить свойству maxPlayerY значение 80, если ,конечно, не хотите дать игроку 80 очков только за то, что начал игру. ;]
Добавьте следующую строчку в метод init(size:) класса GameScene.swift сразу после строки, которая устанавливает цвет фона:
// Reset maxPlayerY = 80 |
Чтобы начислять игроку очки за перемещение вверх экрана, откройте начало метода update: и добавьте следующие строки:
// New max height ? // 1 if Int(player.position.y) > maxPlayerY! { // 2 GameState.sharedInstance.score! += Int(player.position.y) - maxPlayerY! // 3 maxPlayerY = Int(player.position.y) // 4 lblScore.text = String(format: "%d", GameState.sharedInstance.score) } |
Этот код лучше рассмотреть детально:
1. Во-первых, нужно проверить, поднялся ли узел героя выше максимальной отметки, которой достигал в пределах данного цикла игры.
2. Если да, то мы добавляем к счету разницу между текущей высотой и максимальной.
3. Теперь мы устанавливаем новое максимальное значение y.
4. Наконец, мы обновляем лейбл, отображающий счет, в соответствии с новым значением.
Постройте и запустите. Нажмите, чтобы начать. Начните играть и вы увидите, как ваш счет будет увеличиваться по мере продвижения вверх.
Теперь обратим внимание на подсчет звезд. Мы должны увеличивать его каждый раз, когда узел игрока пересекается со звездой, поэтому откройте GameObjectNode.swift и добавьте следующий код в метод collisionWithPlayer класса StarNode сразу после строки, которая начисляет очки:
// Award stars GameState.sharedInstance.stars! += (starType == .Normal ? 1 : 5) |
Это все, что нужно было сделать! Постройте и запустите. Нажмите, чтобы начать. Следите за увеличением количества звезд по мере того, как будете собирать их.
Во время вашей игры вы могли заметить, что когда вы падаете, все пересекаемые героем игровые объекты всё ещё остаются в игре. “Эй!” Вы наверняка думаете: “Я достаточное много играл в Mega Jump, чтобы с уверенностью утверждать, что там такого не было!” Да верно, но это легко исправить.
Напомним, что мы добавляли метод под названием checkNodeRemoval в класс GameObjectNode, который проверяет, нужно ли удалять узел. Пришло время, чтобы вызвать этот метод в каждом кадре.
Добавьте следующий код в метод update класса GameScene.swift, сразу перед строкой, которая проверяет, превышает ли ордината позиции узла героя значение 200:
// Remove game objects that have passed by foregroundNode.enumerateChildNodesWithName("NODE_PLATFORM", usingBlock: { (node, stop) in let platform = node as PlatformNode platform.checkNodeRemoval(self.player.position.y) }) foregroundNode.enumerateChildNodesWithName("NODE_STAR", usingBlock: { (node, stop) in let star = node as StarNode star.checkNodeRemoval(self.player.position.y) }) |
Здесь мы перечисляем все платформы в узле переднего плана и вызываем метод checkNodeRemoval для каждой из них. Потом тоже самое делаем для звёзд.
Постройте и запустите. Нажмите, чтобы начать. Теперь, когда вы будете падать, то не почувствуете под ногами ничего, кроме чистого неба.
Game Over!
Игра заканчивается тогда, когда герой падает в нижнюю часть сцены или поднимается на вершину уровня. В этот момент нам нужно отобразить финальный счет и наивысший балл. Мы осуществим это посредством перехода к сцене конца игры.
Создайте новый Cocoa Touch Class с именем EndGameScene и сделайте его подклассом SKScene.
EndGameScene будет представлять из себя обычный экран, отображающий счет игрока, количество собранных звезд и наивысший балл. Мы добавим все узлы в метод init(size:):, поэтому откройте EndGameScene.swift и замените всё содержимое файла на следующее:
import SpriteKit class EndGameScene: SKScene { required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override init(size: CGSize) { super.init(size: size) // Stars let star = SKSpriteNode(imageNamed: "Star") star.position = CGPoint(x: 25, y: self.size.height-30) addChild(star) let lblStars = SKLabelNode(fontNamed: "ChalkboardSE-Bold") lblStars.fontSize = 30 lblStars.fontColor = SKColor.whiteColor() lblStars.position = CGPoint(x: 50, y: self.size.height-40) lblStars.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Left lblStars.text = String(format: "X %d", GameState.sharedInstance.stars) addChild(lblStars) // Score let lblScore = SKLabelNode(fontNamed: "ChalkboardSE-Bold") lblScore.fontSize = 60 lblScore.fontColor = SKColor.whiteColor() lblScore.position = CGPoint(x: self.size.width / 2, y: 300) lblScore.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Center lblScore.text = String(format: "%d", GameState.sharedInstance.score) addChild(lblScore) // High Score let lblHighScore = SKLabelNode(fontNamed: "ChalkboardSE-Bold") lblHighScore.fontSize = 30 lblHighScore.fontColor = SKColor.cyanColor() lblHighScore.position = CGPoint(x: self.size.width / 2, y: 150) lblHighScore.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Center lblHighScore.text = String(format: "High Score: %d", GameState.sharedInstance.highScore) addChild(lblHighScore) // Try again let lblTryAgain = SKLabelNode(fontNamed: "ChalkboardSE-Bold") lblTryAgain.fontSize = 30 lblTryAgain.fontColor = SKColor.whiteColor() lblTryAgain.position = CGPoint(x: self.size.width / 2, y: 50) lblTryAgain.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Center lblTryAgain.text = "Tap To Try Again" addChild(lblTryAgain) } } |
Здесь много кода, но после всего того, что вы сделали в этом уроке, вас уже ничего не должно пугать.
По сути мы создаем три лейбла, которые будут служить для отображения количества собранных звезд, финального счета и наивысшего балла. Мы присваиваем им значения, взятые из синглтона GameState. Мы также добавляем лейбл, который подсказывает пользователю, что ему нужно коснуться экрана для начала новой игры.
Чтобы отслеживать, закончилась игра или нет, добавьте следующую булевую переменную в свойства класса GameScene.swift:
// Game over dude! var gameOver = false |
В конце игры нужно сохранить текущее состояние в синглтон GameState и осуществить переход к новой сцене. Добавьте следующий метод в GameScene.swift:
func endGame() { // 1 gameOver = true // 2 // Save stars and high score GameState.sharedInstance.saveState() // 3 let reveal = SKTransition.fadeWithDuration(0.5) let endGameScene = EndGameScene(size: self.size) self.view!.presentScene(endGameScene, transition: reveal) } |
Давайте рассмотрим его детально:
1. Сначала мы устанавливаем переменной gameOver значение true.
2. Затем сообщаем синглтону GameState, чтоб он записал состояние игры в файл пользовательских настроек.
3. И, наконец, мы создаём экземпляр класса EndGameScene и делаем переход к нему путём затухания экрана в течение 0,5 секунд.
Метод endGame нужно вызывать, когда узел героя либо падает на дно экрана, либо достигает максимальной высоты уровня. Мы проверяем оба эти случая в методе update главной сцены.
Добавьте следующий код в тот же класс GameScene.swift, в конец метода update:
// 1 // Check if we've finished the level if Int(player.position.y) > endLevelY { endGame() } // 2 // Check if we've fallen too far if Int(player.position.y) < maxPlayerY - 800 { endGame() } |
Взгляните на эти проверки:
Помните, мы загружали endLevelY из листа свойств? Это то значение координаты Y, при которой игрок должен заканчивать уровень.
Если узел героя падает более чем на 800 ниже максимальной отметки, которой он достиг, то игра заканчивается.
Прежде чем запустить игру для того, чтобы увидеть сцену конца игры, мы должны убедиться, что метод endGame не вызывается более одного раза. А это произойдёт, как только метод update() сработает в следующем кадре.
Добавьте следующую строку в начало метода Update () в GameScene.swift:
if gameOver { return } |
Теперь при вызове метода update(), прежде чем сделать переход, он проверяет, закончилась ли игра.
Постройте и запустите. Нажмите, чтобы начать, поиграйте немного, а затем дайте «ультра прыгуну» упасть. Произойдёт переход на сцену конца игры.
Перезапуск
Чтобы иметь возможность перезапустить игру, находясь на конечной сцене, мы должны модифицировать класс EndGameScene так, чтобы он по событию касания осуществлял переход обратно на основную сцену.
Откройте EndGameScene.swift и добавьте следующий метод в этот класс для обработки касаний:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) { // Transition back to the Game let reveal = SKTransition.fadeWithDuration(0.5) let gameScene = GameScene(size: self.size) self.view!.presentScene(gameScene, transition: reveal) } |
Этот код просто создаёт переход на новую сцену GameScene подобно тому, как он осуществлялся при первом запуске приложения.
Остался последний штрих и всё будет готово! Откройте GameScene.swift. В методе init(size:), там где мы сбрасывали значение maxPlayerY, добавьте следующий код:
GameState.sharedInstance.score = 0 gameOver = false |
Мы обнуляем счёт в синглетоне GameState, а также сбрасываем флаг GameOver, чтобы игра могла начаться заново.
Постройте и запустите. Нажмите, чтобы начать и играйте. А теперь постарайтесь побить самый высокий балл! ;]
Что можно сделать еще?
Поздравляю, вы создали игру в стиле Mega Jump!
Под статьей вы найдете ссылку на готовый проект с полным кодом уроков.
В четырех частях мы охватили весь процесс создания игры на физическом движке. Вы многое узнали о том, как настроить обнаружения столкновений и как построить свою игровую логику для обработки столкновений.
Но вы можете сделать гораздо больше! Откройте файл конфигурации plist и подумайте о новых уровнях и возможностях, которые сможете реализовать. Просто скопируйте исходный plist и создавайте любое количество уровней, меняя конфигурацию внутри файла.
Сыграйте в Mega Jump и вдохновитесь! Сможете ли вы воплотить идеи, которые будут лучше, чем в оригинальной игре?
И последнее, но не менее важное: если вы хотите узнать больше о Sprite Kit, прочитайте книгу iOS Games by Tutorials, в которой вы узнаете, как сделать пять полных игр: от «зомби экшинов» до автомобильных гонок и перестрелок в космосе!
И, конечно, если у вас есть какие-либо вопросы или комментарии, то пожалуйста, присоединяйтесь к обсуждению.
Скачать готовый проект и код игры.
|
|