Създайте първата си игра на iPhone от нулата, не се изисква опит с кодиране.

Въведение

Този урок е написан за Swift 4.0 / XCode 9, всяка бъдеща версия може да има проблеми, докато не актуализирам статията.

Значи се интересувате от изграждането на мобилни игри? Може би е примамливостта да излитате и купувате нов автомобил или просто е страстта да създадете своя собствена игра. Какъвто и да е мотивът ви, този урок ще ви преведе през стъпките за настройване на вашия компютър за изграждане на приложения и създаване на проста игра от нулата! Целият код на играта е включен в урока; за завършване на този проект не са необходими предварителни познания по програмиране.

След като завършите този урок, ще можете да стартирате приложение на IOS със собствена конструкция на вашето устройство или симулатор на устройство. Ще разберете основите на проектирането на игра от нулата, как да запишете данни на вашето устройство, след като приложението ви се затвори, как да изобразите спрайтове на екрана и ще започнете да разбирате как да използвате двигателя на играта SpriteKit. Ще ви преведа и как съм проектирал играта Snake и как можете да започнете да изграждате игра по ваш собствен дизайн!

Ето линк за изтегляне към леко модифицирана версия на тази игра, налична в App Store: https://itunes.apple.com/us/app/minimal-snake/id1355406338?mt=8 (напълно безплатно, без реклами).

Забележка: В цялата статия използвах термина copy / paste, читател ми посочи, че това е лоша практика и съм напълно съгласен. Ако искате бързо да копирате кода и да създадете функциониращ продукт, който е добре, обаче, най-вероятно ще получите повече от записването на всеки ред на ръка!

Завършен продукт

Ето видео, демонстриращо какво ще сте изградили и инсталирали на телефона си до края на този урок!

Приготвяме се да започнем

За да следвате този урок, ще трябва да създадете акаунт за разработчици на Apple и да изтеглите Xcode (програмата, използвана за изграждане на приложения за IOS). За съжаление Xcode е наличен само за Macs; ако имате машина с Windows / Linux тук е уебсайт, който може да ви помогне да настроите Xcode.

Тези следващи стъпки ще преминат през регистрация за безплатен акаунт на програмист и инсталиране на Xcode. Ако вече имате акаунт и Xcode, можете да преминете към следващия раздел. За да започнете, първо посетете developer.apple.com и щракнете върху членския център, след което влезте с вашия Apple ID. Отидете на страницата на Apple Developer Agreement и приемете споразумението; вече имате безплатен акаунт за разработчици! За да качите проектите си в магазина за приложения, ще трябва да платите 100 $ годишна такса.

Сега, когато имате акаунт на програмист, трябва да инсталирате Xcode. Xcode е достъпен чрез Mac App Store. След като инсталирате Xcode стартирайте програмата и кликнете върху Xcode -> Preferences -> Accounts -> + и изберете Add Apple ID. Влезте с Apple ID, използван за регистрация за акаунта на програмист. Поздравления, сега можете да тествате приложенията си в iPhone симулатор или да ги стартирате на вашето лично устройство!

Започване на проекта

Сега, след като сте се регистрирали за акаунт на програмист и сте инсталирали Xcode, можете да започнете да разработвате първата си мобилна игра!

Стартирайте Xcode и кликнете върху „Създаване на нов Xcode проект“.

Кликнете върху шаблона „Игра“.

Въведете името „Snake“ (или каквото искате) за вашата игра. Изберете име на организация, ако имате уебсайт, можете да въведете това обратно (com.gavinshrader) или можете просто да използвате името си като идентификатор. Уверете се, че езикът е зададен на “Swift” и технологията на играта е “SpriteKit”. Премахнете отметката от 3 квадратчета, ако са избрани.

Щракнете с десния бутон върху „Actions.sks“ и преминете към кошчето. Отидете на GameScene.sks и кликнете върху текста "Hello World", след което го изтрийте. Отидете на GameScene.swift и премахнете целия предварително изграден код, така че файлът ви да съответства на изображението по-долу.

Създайте нов файл Swift, като отидете на File -> New File и щракнете върху Swift File, или като щракнете с десния бутон на вашата папка на проекта („Snake“) и изберете нов файл. Намерете иконата на файла Swift, която е маркирана по-долу, ако не присъства, въведете „Swift“ във филтърната лента. Въведете името „GameManager“ и се уверете, че вашият проект („Snake“) е избран под цели, щракнете върху „Create“, за да създадете вашия нов бърз файл.

Изграждане на менюто на играта

Преди да започнем кодирането, проверете дали вашият проект се компилира след промените, които сте направили в последния раздел. Изберете устройство от списъка на симулатора, кликнете върху бутона, където е „iPhone 6“, вероятно ще бъде обозначен с „Generic iOS устройство“. Ако искате да тествате на физическо устройство, включете вашия iPhone, дайте Xcode няколко минути и щракнете върху вашето устройство. След като направите това, кликнете върху триъгълния бутон за изпълнение. Ако сте избрали симулирано устройство, този екран трябва да изскочи:

Ако на екрана се показва „Hello World“, уверете се, че сте изтрили етикета, като отидете на GameScene.sks, щракнете върху етикета и след това изберете изтриване.

Най-накрая сме готови да започнем да изграждаме играта! Когато започнете игра, тя помага предварително да подредите екраните си. В тази игра ще започнем с прост екран от менюто, който показва заглавието / логото на играта. Бутон за игра ще стартира геймплей екран с игрална зона и два етикета за текущия ви резултат и най-добър резултат. Когато умрете, екранът за край на играта ще се покаже с опция за игра отново.

За да стартираме нашата игра, първо трябва да съставим меню за стартиране на играта. Ще започнем с писане на код, за да инициализираме меню, като добавим заглавието на играта, етикет „най-добър резултат“ и бутон за игра. Отворете файла GameScene.swift и копирайте целия код отдолу, така че файлът да съвпада с изображението (Фигура А).

// 1
вар играLogo: SKLabelNode!
var bestScore: SKLabelNode!
var playButton: SKShapeNode!
// 2
initializeMenu ()
// 3
private func InitializeMenu () {
    // Създаване на заглавие на играта
    gameLogo = SKLabelNode (fontNamed: "ArialRoundedMTBold")
    gameLogo.zPosition = 1
    gameLogo.position = CGPoint (x: 0, y: (frame.size.height / 2) - 200)
    gameLogo.fontSize = 60
    gameLogo.text = "SNAKE"
    gameLogo.fontColor = SKColor.red
    self.addChild (gameLogo)
    // Създаване на етикет за най-добър резултат
    bestScore = SKLabelNode (fontNamed: "ArialRoundedMTBold")
    bestScore.zPosition = 1
    bestScore.position = CGPoint (x: 0, y: gameLogo.position.y - 50)
    bestScore.fontSize = 40
    bestScore.text = "Най-добър резултат: 0"
    bestScore.fontColor = SKColor.white
    self.addChild (bestScore)
    // Създаване на бутон за игра
    playButton = SKShapeNode ()
    playButton.name = "play_button"
    playButton.zPosition = 1
    playButton.position = CGPoint (x: 0, y: (frame.size.height / -2) + 200)
    playButton.fillColor = SKColor.cyan
    нека topCorner = CGPoint (x: -50, y: 50)
    нека bottomCorner = CGPoint (x: -50, y: -50)
    нека средно = CGPoint (x: 50, y: 0)
    нека път = CGMutablePath ()
    path.addLine (към: topCorner)
    path.addLines (между: [topCorner, bottomCorner, средата])
    playButton.path = път
    self.addChild (playButton)
}
Фигура А

Компилирайте кода и проверете дали устройството ви показва изображението отгоре. Ето обяснение за случващото се тук, това може да изглежда като стена от код, но е лесно да се разбере, когато го разбиете.

  • 1: Създаваме променливи за логовете / бутоните. „!“ След името на променливата означава, че трябва да инициализираме променливите, те не могат да бъдат празни или „нулеви“.
  • 2: Обаждаме се на функцията „InitializeMenu ()“, след като изгледът на играта се зареди. didMove (към: view: SKView) е функцията, която се извиква след като GameScene се зареди.
  • 3: Това е функцията intializeMenu (), която написахме за създаване на обекти от менюто.
  • 4/5/6: Създайте обектите и добавете към GameScene, като извикате „self.addChild ()“.
  • 7: Избрах да използвам SKShapeNodes за този проект поради тяхната простота, това е алтернатива на създаването на вашата графика в редактор на изображения. Този ред код създава път във формата на триъгълник. Моля, обърнете внимание, че ако планирате да създадете и публикувате приложение, трябва да използвате SKSpriteNodes, за да заредите създадено от вас изображение, ShapeNodes може да причини проблеми с производителността, когато се използват в големи количества, тъй като те се динамично изтеглят веднъж за кадър.
  • 8: Задайте триъгълния път, който създадохме към спрайта playButton и добавете към GameScene.

Игра на играта

Сега, когато имаме проста настройка на менюто, нека да работи бутона за възпроизвеждане Първо отидете на вашия GameManager.swift файл и заменете целия код с този, така че да съвпада с изображението по-долу (Фигура Б).

импортиране на SpriteKit
клас GameManager {
}
Фигура Б

Копирайте кода по-долу във вашия GameScene.swift файл, така че да съвпада с изображението по-долу (Фигура C).

// 1
вар игра: GameManager!
// 2
игра = GameManager ()
// 3
отмени функционалните докосванияБег (_ докосва: Задайте , със събитие: UIEvent?) {
    за допир при докосвания {
        нека location = touch.location (in: self)
        нека touchchedNode = self.nodes (на: местоположение)
        за възел в touchedNode {
            ако node.name == "play_button" {
                Започни игра()
            }
        }
    }
}
// 4
private func startGame () {
    печат ("стартиране на играта")
}
Фигура С
  • 1: Инициализирайте обект GameManager. Повече за това по-късно ... това ще съдържа данни за резултата и ще управлява движението на играча.
  • 2: Задайте променливата на играта на нов GameManager () обект.
  • 3: Тази функция се извиква от игровия двигател всеки път, когато потребителят докосне екрана. Спомнете си, че бутонът за пускане, който създадохме по-рано, има името „play_button“. С помощта на името можем да проверим дали потребителят е докоснал SpriteNode с името „play_button“, след като това се случи, ние извикваме функцията startGame () от точка 4 на куршума.
  • 4: Тази функция стартира играта.

Уверете се, че кодът ви работи правилно, като стартирате приложението си и кликнете върху триъгълния бутон за възпроизвеждане. Ако вашите докосвания са правилно измерени, тогава конзолата трябва да показва "стартиране на играта", както е отбелязано на изображението по-долу (Фигура D).

Фигура D

Ако конзолата ви не се показва, отидете на горната лента и кликнете върху „Помощ“, в лентата за търсене въведете „Конзола“ и след това кликнете върху „Област за отстраняване на грешки> Активиране на конзолата“. Вече имаме работеща система от меню и бутон за игра, тук наистина можем да започнем да се забавляваме.

Зареждане на изгледа на играта

Така че сега имаме бутон за възпроизвеждане, който може да задейства функция, какво да правим? За да покажем изгледа на играта, първо трябва да скрием бутоните на менюто. Добавете този ред код, за да скриете бутоните на менюто си с обикновена анимация. Кодът ви трябва да съвпада с изображението по-долу (Фигура Е).

//Започни играта
private func startGame () {
    печат ("стартиране на играта")
    // 1
    gameLogo.run (SKAction.move (от: CGVector (dx: -50, dy: 600), продължителност: 0.5)) {
    self.gameLogo.isHidden = true
    }
    // 2
    playButton.run (SKAction.scale (до: 0, продължителност: 0,3)) {
        self.playButton.isHidden = true
    }
    // 3
    нека bottomCorner = CGPoint (x: 0, y: (frame.size.height / -2) + 20)
    bestScore.run (SKAction.move (до: bottomCorner, продължителност: 0.4))
}
Фигура ЕФигура F
  • 1: Преместете игратаLogo от екрана и след това я скрийте от изгледа. Скобите след SKAction стартират, когато действието приключи. Например, ако изпълним SKAction с продължителност 10, кодът в скобата ще се стартира след 10 секунди. Ето един пример:
exampleNode.run (SKAction.move (от: CGVector (dx: 0, dy: 0), продължителност: 10) {
    отпечатване („Достигнах след 10 секунди“)
}
  • 2: Мащабиране на бутона за игра на 0; това действие свива бутона и след това го скрива от изглед.
  • 3: Преместете етикета с най-добри резултати в долната част на екрана.

Вашето меню сега трябва да се държи като този gif (фигура F), когато щракнете върху бутона за възпроизвеждане!

Сега ще започнем да проектираме действителната „змийска“ част от тази игра, започнете с добавяне на тези редове от код, така че вашият код да съвпада с изображението по-долу (Фигура G).

// 1
var currentScore: SKLabelNode!
var playerPositions: [(Int, Int)] = []
вар играBG: SKShapeNode!
var gameArray: [(възел: SKShapeNode, x: Int, y: Int)] = []
// 2
initializeGameView ()
// 3
private func InitializeGameView () {
    // 4
    currentScore = SKLabelNode (fontNamed: "ArialRoundedMTBold")
    currentScore.zPosition = 1
    currentScore.position = CGPoint (x: 0, y: (frame.size.height / -2) + 60)
    currentScore.fontSize = 40
    currentScore.isHidden = true
    currentScore.text = "Резултат: 0"
    currentScore.fontColor = SKColor.white
    self.addChild (currentScore)
    // 5
    нека ширина = frame.size.width - 200
    нека височина = frame.size.height - 300
    нека rect = CGRect (x: -width / 2, y: -height / 2, width: width, height: height)
    gameBG = SKShapeNode (rect: rect, cornerRadius: 0.02)
    gameBG.fillColor = SKColor.darkGray
    gameBG.zPosition = 2
    gameBG.isHidden = true
    self.addChild (gameBG)
    // 6
    createGameBoard (ширина: ширина, височина: височина)
}
Фигура GФигура G
  • 1: Нови променливи! Ние създаваме етикет, който да показва текущия резултат, масив от всички позиции, които "змията" или играчът има в момента, фон за нашия изглед на играта и масив за проследяване на позициите на всяка клетка в изгледа на играта.
  • 2: Обадете се на функцията InitializeGameView ().
  • 3: Инициализира изгледа на играта.
  • 4: Добавете етикета на текущия резултат на екрана, това е скрито, докато не напуснем менюто си.
  • 5: Създайте ShapeNode, който да представя зоната за игра на нашата игра. Това е мястото, където змията ще се движи наоколо.
  • 6: Създайте дъската за игри. Тази функция инициализира един тон квадратни клетки и ги добавя към игралната дъска.

След това искаме да създадем масив от клетки, които ще използваме за изобразяване на змията и точките на екрана. Създайте функцията createGameBoard от кода по-долу, така че да съвпада с Фигура З.

// създайте игрална дъска, инициализирайте масив от клетки
private func createGameBoard (ширина: Int, височина: Int) {
    нека ширина на клетката: CGFloat = 27.5
    нека numRows = 40
    нека numCols = 20
    var x = CGFloat (ширина / -2) + (ширина на клетката / 2)
    var y = CGFloat (височина / 2) - (cellWidth / 2)
    // прокарайте редове и колони, създайте клетки
    за i in 0 ... numRows - 1 {
        за j в 0 ... numCols - 1 {
            нека CellNode = SKShapeNode (rectOf: CGSize (ширина: ширина на клетката, височина: ширина на клетката))
            cellNode.strokeColor = SKColor.black
            cellNode.zPosition = 2
            cellNode.position = CGPoint (x: x, y: y)
            // добавяне към масив от клетки - след това добавяне към дъската за игри
            gameArray.append ((възел: cellNode, x: i, y: j))
            gameBG.addChild (cellNode)
            // итерация x
            x + = ширина на клетката
        }
        // нулиране на x, повторете y
        x = CGFloat (ширина / -2) + (ширина на клетката / 2)
        y - = ширина на клетката
    }
}
Фигура Н

Вашият код трябва да съвпада с кода отгоре, след като стартирате играта, нищо няма да се промени. Ако искате да видите дъската за игри като на екрана на симулатора по-горе, добавете следния код към стартовата си игрална функция, така че да съвпада с Фигура I.

bestScore.run (SKAction.move (до: bottomCorner, продължителност: 0.4)) {
    self.gameBG.setScale (0)
self.currentScore.setScale (0)
self.gameBG.isHidden = false
self.currentScore.isHidden = false
self.gameBG.run (SKAction.scale (до: 1, продължителност: 0,4))
self.currentScore.run (SKAction.scale (до: 1, продължителност: 0,4))
}

Кратко обяснение на метода createGameBoard, преди да продължим. Този метод преминава през 40 реда и 20 колони, за всеки ред / позиция на колоната създаваме нова квадратна кутия или „cellNode“ и добавяме това към сцената. Ние също добавяме тази cellNode в масив „gameArray“, така че да можем лесно да прикачите ред и колона към съответната клетка.

Фигура I - Показва нова игрална дъска!

Създаване на игрови инстанция

Сега имаме работещ бутон за игра, кутия, пълна с по-малки кутии и някои етикети. Как да превърнем това в игра, която всъщност е забавна за игра? Първо ще ни трябва обект, който да проследи местоположението на „змия“ на екрана, за да можем да се движим. Отворете класа GameManager.swift и създайте следните методи. Също така добавете тази промяна (// 1) във функцията didMove (за да видите: SKView) в GameScene.swift, така че вашият код да съвпада с фигура J.

// 1 - GameScene.swift
игра = GameManager (сцена: самостоятелно)
// 2 - GameManager.swift
клас GameManager {
    
    вар сцена: GameScene!
    init (сцена: GameScene) {
        self.scene = сцена
    }
}
Фигура J

Правейки тези промени казваме, че GameManager трябва да съдържа препратка към класа GameScene, след като се инициализира. Сега клас GameManager може да комуникира с GameScene, като се обади на сцената. Например, scene.startGame () ще изпълнява функцията за стартиране на играта от контрола на GameManager класа.

Сега сме готови да заредим плейъра в GameView. Първо добавете следния фрагмент на код във вашия GameScene.swift файл в метода startGame () вътре в скобите на bestScore.run () {} Този метод ще извика функцията initGame, след като етикетът за най-добър резултат завърши SKAction.

// нов код
self.game.initGame ()
Фигура К

Сега отидете на GameManager.swift и добавете следните методи под метода init (сцена: GameScene), така че вашият код да съвпада с Фигура L.

// 1
func initGame () {
    // начална позиция на играча
    scene.playerPositions.append ((10, 10))
    scene.playerPositions.append ((10, 11))
    scene.playerPositions.append ((10, 12))
    renderChange ()
}
// 2
func renderChange () {
    за (възел, х, у) в scene.gameArray {
        ако съдържа (a: scene.playerPositions, v: (x, y)) {
            node.fillColor = SKColor.cyan
        } else {
            node.fillColor = SKColor.clear
        }
    }
}
// 3
func съдържа (a: [(Int, Int)], v: (Int, Int)) -> Bool {
    нека (c1, c2) = v
    за (v1, v2) в {if v1 == c1 && v2 == c2 {return true}}
    върнете невярно
}
Фигура L
  • 1: функция initGame (). Това добавя 3 координати към масива на PlayerPositions на GameScene,
  • 2: функция renderChange (). Ще наричаме този метод всеки път, когато преместваме „змия“ или играч. Това прави всички празни квадратчета ясни, а всички квадратчета, където играчът е разположен, са сини.
  • 3: Това е проста функция, която проверява дали кортеж (бърза структура от данни, която може да съдържа комбинация от типове под формата на (Int, CGFloat, Int, String) ... и т.н.), съществува в масив с кортежи. Тази функция проверява дали масивът playerPositions съдържа въведените координати от масива от клетки на GameScene. Това не е непременно най-ефективният начин за правене на неща, тъй като ние проверяваме всяка отделна клетка по време на всяка актуализация. Ако искате да предизвикате себе си, опитайте се да актуализирате кода, така че да променя само квадратчетата от масива playerPositions!

Преместване на играча

Фигура М

Сега нашият играч е изобразен на екрана и възможността да изобразява произволен брой позиции. Ако добавите повече координати към масива playerPositions, тогава повече квадратчета ще бъдат оцветени циан. По време на играта ние искаме постоянно да движим „змията“ в една посока, докато играчът плъзне по екрана, за да промени посоките. Ето решетка, показваща координатите на нашата мрежа, за да можете лесно да разберете как работят координатите зад кулисите (Фигура М).

Както можете да видите по отвратително малките етикети, горният ляв ъгъл е 0,0, а долният десен ъгъл е 39,19. Това означава, че ако искаме да движим нашия играч в посоките наляво, надясно, нагоре и надолу, правим това, като прилагаме следната основна алгебра (Фигура N).

Фигура N

Както можете да видите, лявата / дясната посока съвпадат с тази на типична координатна равнина; лявата е отрицателна, а дясната е положителна. Въпреки това, за да се движим нагоре по координатната равнина, ние искаме да намалим y и да се придвижим надолу, ние искаме да увеличим y. Това се дължи на факта, че нашата for loop във функцията createGameBoard стартира в горната част и работи надолу.

Сега, когато разбирате посоката на дъската, можете да приложите метод, който движи плейъра. Ако отворите файла GameScene.swift, ще забележите удобен метод, наречен актуализация (_ currentTime: TimeInterval). В контура на изобразяване функцията за актуализиране се извиква веднъж в секунда. Това означава, че ако приложението ви работи с 60 fps, функцията се нарича 60 пъти в секунда, ако играта работи с 40 fps, тя се нарича само 40 пъти в секунда. Вътре в функцията за актуализиране добавете този ред код, така че вашият код да съвпада с Фигура О.

// 1
game.update (време: currentTime)
Фигура О

След като добавите този код, трябва да се появи червена грешка, за да коригирате тази навигация към вашия GameManager.swift файл и добавете тези редове от код, така че файлът ви да съвпада с Фигура П.

// 1
var nextTime: Двойна?
var timeExtension: Double = 1
// 2
актуализация на функцията (време: двойно) {
    ако nextTime == nil {
        nextTime = време + timeExtension
    } else {
        ако времето> = nextTime! {
            nextTime = време + timeExtension
            печат (време)
        }
    }
}
Фигура P

След стартиране на приложението ви конзолата трябва да отпечатва нов път всяка секунда. Ето бърз анализ на това, което прави този код.

  • 1: инициализирайте две нови променливи. nextTime е интервалът nextTime, на който ще отпечатаме изявление на конзолата, timeExtension е колко дълго ще чакаме между всеки отпечатък (1 секунда).
  • 2: Тази функция за актуализиране се нарича 60 пъти в секунда, искаме да актуализираме позицията на играча само веднъж в секунда, така че играта да не е смешно бърза. За да постигнем това, проверяваме дали е зададено nextTime. Както можете да видите от // 1, nextTime е инициализиран като незадължителна стойност. „? „След Double казва на бързия компилатор, че искаме nextTime да бъде двойник и че МОЖЕ да бъде настроен на нула. Когато се извика функцията за актуализиране, първо проверяваме дали nextTime е зададен, ако не е зададен, ние го задаваме на текущото време + разширението time (1 секунда). След като текущото време затъмни „nextTime“, след това увеличаваме nextTime с 1 секунда. Тази функция сега приема функция за нередовно обновяване (около 30–60 пъти / секунда) и генерира изход само веднъж в секунда.

Сега имаме функция, която работи веднъж в секунда, ако искате да увеличите скоростта на играта си, просто намалете timeExtension до стойност, по-голяма от 0, ако искате да забавите играта си, след това увеличете стойността на timeExtension. (Забележка: „1” == 1 секунда за разширяване на време).

Сега искаме да преместим плейъра по екрана, добавете следния код, така че файлът ви да съвпада с фигура Q. Освен това премахнете реда „печат (време)“ от функцията за актуализиране, която току-що създадохме в GameManager.swift, това ще спами вашия конзола и беше наистина полезен само за тестване на валидността на вашия код.

// 1
var playerDirection: Int = 1
// 2
updatePlayerPosition ()
// 3
private func updatePlayerPosition () {
    // 4
    var xChange = -1
    var yChange = 0
    // 5
    превключвател playerDirection {
        случай 1:
            //наляво
            xChange = -1
            yChange = 0
            почивка
        случай 2:
            // нагоре
            xChange = 0
            yChange = -1
            почивка
        случай 3:
            // дясно
            xChange = 1
            yChange = 0
            почивка
        случай 4:
            // надолу
            xChange = 0
            yChange = 1
            почивка
        по подразбиране:
            почивка
    }
    // 6
    ако scene.playerPositions.count> 0 {
        var start = scene.playerPositions.count - 1
        докато започнете> 0 {
            scene.playerPositions [старт] = scene.playerPositions [старт - 1]
            старт - = 1
        }
        scene.playerPositions [0] = (scene.playerPositions [0] .0 + yChange, scene.playerPositions [0] .1 + xChange)
    }
    // 7
    renderChange ()
}
Фигура Q

След като добавите този код, вашата игра трябва да изглежда като gif от Figure Q (задайте playerDirection на 4, за да получите същата посока на движение). Две неща веднага ми се откроиха, когато написах това; първо, змията се движи болезнено бавно, може би трябва да увеличим скоростта на играта от 1 секунда до 1/2 или 1/4 от секундата. Второ, какво ще правим, когато змията удари стена? В някои версии на змия играчът се изкривява около екрана, в други версии сблъсъкът със стената води до смърт. Харесва ми вида на екранна основа, така че мисля, че ще използваме този метод за тази игра. Сега обяснение на този код, който току-що написахте:

  • 1: Създайте променлива, която се използва за определяне на текущата посока на играча. В кода променливата е настроена на 1, в gif на фигура Q зададох посоката на 4. Променете тази променлива, за да видите всички различни посоки.
  • 2: Премахнахме печата (време) и го заменихме с призив за updatePlayerPosition (), в тази итерация ние призоваваме актуализацията всяка секунда.
  • 3: Този метод премества плейъра или „змия“ по екрана.
  • 4: Задайте променливи, за да определите промяната, която трябва да направим спрямо x / y отпред на змията.
  • 5: Това е команда за превключване, приема входа на playerPosition и променя променливите x / y в зависимост от това дали играчът се движи нагоре, надолу, наляво или надясно.
  • 6: Този блок код премества позициите напред в масива. Искаме да преместим предната част на опашката в подходящата посока и след това да преместим всички опашни блокове напред в следващата позиция.
  • 7: Представете промените, които направихме в масива от позиции.

Изкривяване на змията около екрана

Сега имаме движещ се играч, приятна работа! Първо ще искаме да увеличим скоростта на играта, оказва се, че чакането 1 секунда е просто твърде бавно, за да бъде забавно. Това е урок, който ще научите в дизайна на играта, има много настройка и малки промени, които ще трябва да направите, за да усъвършенствате усещането на играта. Когато работя по проект, често прекарвам по-голямата част от времето си в незначителни промени, за да настроите усещането, трябва да усъвършенствате своята механика, за да изградите забавна игра; след като имате перфектна механика, можете да работите върху добавяне на фантастични допълнения като частици и звуци.

Променете променливата timeExtension на 0,15 и компилирайте вашия проект.

// 1 - GameManager.swift
var timeExtension: Double = 0,15

Сега можем да започнем да изкривяваме змията около екрана, добавяме следния код, така че вашият проект да съвпада с Фигура R. Забележете, този код е добавен към функцията updatePlayerPosition () в GameManager.swift, която току-що написахме.

// 1
ако scene.playerPositions.count> 0 {
    нека x = scene.playerPositions [0] .1
    нека y = scene.playerPositions [0] .0
    ако у> 40 {
        scene.playerPositions [0] .0 = 0
    } else, ако y <0 {
        scene.playerPositions [0] .0 = 40
    } else, ако x> 20 {
       scene.playerPositions [0] .1 = 0
    } else, ако x <0 {
        scene.playerPositions [0] .1 = 20
    }
}
Фигура R

При компилирането на приложението ви екранът трябва да съответства на gif от Фигура R, използвах playerDirection 4 в GIF. Змията вече може да се изкривява от всяка страна на екрана.

  • 1: Този код е доста прост, той проверява дали позицията на главата на змията е преминала горната, долната, лявата или дясната страна и след това премества плейъра в другата страна на екрана.

Контрол на движението на Змията с помощта на пръсти с жестове

Играта ни идва заедно, сега се нуждаем от метод за контрол на посоката на змията. За да изпълним това, ще използваме жестове с пръст, за да се движим наляво, надясно, нагоре и надолу. Добавете този код към вашия GameScene.swift файл, така че да съвпада с Фигура S.

// 1
нека swipeRight: UISwipeGestureRecognizer = UISwipeGestureRecognizer (цел: самостоятелно, действие: #selector (swipeR))
swipeRight.direction = .право
view.addGestureRecognizer (swipeRight)
нека swipeLeft: UISwipeGestureRecognizer = UISwipeGestureRecognizer (цел: самостоятелно, действие: #selector (swipeL))
swipeLeft.direction =. наляво
view.addGestureRecognizer (swipeLeft)
нека swipeUp: UISwipeGestureRecognizer = UISwipeGestureRecognizer (цел: самостоятелно, действие: #selector (swipeU))
swipeUp.direction = .up
view.addGestureRecognizer (swipeUp)
нека swipeDown: UISwipeGestureRecognizer = UISwipeGestureRecognizer (цел: самостоятелно, действие: #selector (swipeD))
swipeDown.direction = .надолу
view.addGestureRecognizer (swipeDown)
// 2
@objc func swipeR () {
    печат ( "R")
}
@objc func swipeL () {
    печат ( "L")
}
@objc func swipeU () {
    печат ( "ф")
}
@objc func swipeD () {
    печат ( "D")
}
Фигура S
  • 1: Добавете жестове на пръст към функцията didMove (за преглед: SKView).
  • 2: Създайте функции, които се извикват, когато потребителят въведе жест на прекарване на пръст. „@Objc“ преди функцията да създаде функция-c, това е необходимо, за да бъде извикан чрез #selector в оригиналния UISwipeGestureRecognizer.

Тествайте кода си, като компилирате приложението си и след това плъзнете във всичките четири посоки, конзолата ви трябва да отпечата съответното писмо за всеки жест на пръст. Сега, когато имаме настроени разпознавачи на жестове, трябва да променим посоката на движение на играча, да заменим отчетите за печат във функциите за прекарване на пръст с този код и да добавим код към GameManager.swift, така че вашият проект да съвпада с Фигура Т.

// 1 - GameScene.swift
game.swipe (ID: 3)
game.swipe (ID: 1)
game.swipe (ID: 2)
game.swipe (ID: 4)
// 2 - GameManager.swift
прекарване на пръст (ID: Int) {
    ако! (ID == 2 && playerDirection == 4) &&! (ID == 4 && playerDirection == 2) {
        ако! (ID == 1 && playerDirection == 3) &&! (ID == 3 && playerDirection == 1) {
            playerDirection = ИД
        }
    }
}
Фигура ТФигура Т
  • 1: След като се установи преместване с жест, се уведомява класът на gameManager.
  • 2: Ако прекарването на пръст не е в противоречие с текущата посока, задайте посоката на плейъра на прекара на пръст. Ако се движите надолу, не можете веднага да се придвижите нагоре. Ако се движите наляво, не можете внезапно да се движите надясно. В някои версии за въвеждане на змия при неправилен ход като този може да се стигне до смърт, но в тази версия просто ще игнорираме външни данни.

Добавяне на точки към играта и коригиране на резултата

Вече имаме работеща система от менюта, която отваря дъска за игри, масив от клетки, масив от позиции на играча, играч, който може да се движи по екрана и да се завърта по краищата и плъзгащ разпознавател за контроли. Сега трябва да добавим механизъм за оценка, така че играта да е забавна. Този механизъм ще генерира случайни точки, които ще увеличат резултата и ще удължат следите на „змията“ или играча.

Първа стъпка: Генерирайте произволна точка и я изобразете на екрана. Добавете следния код към вашия GameScene.swift файл, така че да съвпада с Фигура U.

// 1
вар резултатPos: CGPoint?
Фигура U

Сега отидете на вашия GameManager.swift файл и добавете следния код, така че да съвпада с Фигура V.

// 2
generateNewPoint ()
// 3
private func generatorNewPoint () {
    нека randomX = CGFloat (arc4random_uniform (19))
    нека randomY = CGFloat (arc4random_uniform (39))
    scene.scorePos = CGPoint (x: randomX, y: randomY)
}
// 4
ако scene.scorePos! = nil {
    ако Int ((scene.scorePos? .x)!) == y && Int ((scene.scorePos? .y)!) == x {
        node.fillColor = SKColor.red
    }
}
Фигура V

След стартиране на кода симулаторът ви трябва да показва произволно поставен червен квадрат.

  • 1: Инициализирайте променлива за позицията на случаен резултат. „? „Показва, че това е нула (празно или все още не е зададено), докато не зададем променливата по-късно.
  • 2: Извикайте функцията вътре в функцията initGame (), която ще генерира нова случайна точка.
  • 3: Тази функция генерира произволна позиция в границите на дъската (20/40), масивите започват да се броят от 0, така че броим от 0 до 19 и от 0 до 39, това е масив 20x40.
  • 4: Вътре в контура на визуализация проверяваме дали позицията на текущия възел съвпада с тази на случайно поставената оценка, ако имаме съвпадение, тогава задаваме цвета на червено. Можете да промените цвета според вашите желания. Променливата, която запазва позицията на резултата, е CGPoint, това означава, че трябва да проверим point.x и point.y и да я сравним с местоположенията на x и y на текущия възел. Забележете, позициите x / y са обърнати в масива от възли, затова сравняваме x == y и y == x.

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

Добавете следния код към вашия GameManager.swift файл, така че да съвпада с Фигура W.

// 1
var currentScore: Int = 0
// 2
checkForScore ()
// 3
private func checkForScore () {
    ако scene.scorePos! = nil {
        нека x = scene.playerPositions [0] .0
        нека y = scene.playerPositions [0] .1
        ако Int ((scene.scorePos? .x)!) == y && Int ((scene.scorePos? .y)!) == x {
            currentScore + = 1
            scene.currentScore.text = "Резултат: \ (текущ Резултат)"
            generateNewPoint ()
         }
     }
}
// 4
докато съдържа (a: scene.playerPositions, v: (Int (randomX), Int (randomY))) {
    randomX = CGFloat (arc4random_uniform (19))
    randomY = CGFloat (arc4random_uniform (39))
}
Фигура W
  • 1: Инициализирайте променлива, за да проследите текущия резултат в тази игра.
  • 2: Извикайте функцията checkForScore () във функцията за актуализиране, която се извиква всеки път, когато играчът се движи.
  • 3: Тази функция проверява дали е зададен резултатPos, ако има след това проверява главата на змията. Ако змията докосне точка, резултатът се повтаря, текстовият етикет, показващ резултата, се актуализира и се генерира нова точка.
  • 4: Добавих този код в метода geneNewPoint (), за да гарантирам, че в тялото на змията не се генерира точка. Тъй като змията нараства на дължина, по-вероятно е да се сблъскаме с този проблем, така че този кодов блок трябва да реши този проблем.

След пускане на кода ще забележите, че при попадение на резултат ще се генерира нов резултат на дъската и ще се повтори етикета на резултата. Сега трябва да увеличим дължината на змията, така че механиката на играта да е по-близо до завършване. Това се оказва невероятно просто, просто добавете този кодов фрагмент към вашата checkForScore () функция, така че вашият код да съвпада с Фигура X.

scene.playerPositions.append (scene.playerPositions.last!)
scene.playerPositions.append (scene.playerPositions.last!)
scene.playerPositions.append (scene.playerPositions.last!)
Фигура X

Край на играта

Сега трябва да внедрим метод, който завършва играта и се връща в система от менюта. В играта на змия играта приключва, след като играчът се сблъска в собствената си опашка. Можем да постигнем този ефект, като приложим следните редове код във файла GameManager.swift. Уверете се, че вашият код съвпада с фигура Y.

// 1
checkForDeath ()
// 2
private func checkForDeath () {
    ако scene.playerPositions.count> 0 {
        var arrayOfPositions = scene.playerPositions
        нека headOfSnake = arrayOfPositions [0]
        arrayOfPositions.remove (при: 0)
        ако съдържа (a: arrayOfPositions, v: headOfSnake) {
            playerDirection = 0
        }
    }
}
// 3
ако playerDirection! = 0 {
    playerDirection = ИД
}
// 4
случай 0:
    // мъртъв
    xChange = 0
    yChange = 0
    почивка
Фигура YФигура Y
  • 1: Обадете се на функцията checkForDeath ().
  • 2: Проверете дали главата на играча се е сблъскала с някоя от позициите на опашката. Ако играчът е умрял, тогава поставете playerDirection на 0.
Фигура Z
  • 3: Ако играчът е починал (playerDirection = 0), тогава не допускайте нови жестове като прекарване на пръст като вход.
  • 4: Добавете нов случай в оператора за превключване в updatePlayerPosition (), ако playerDirection е настроен на 0, не променяйте позицията на главата. Това ще позволи на позициите на опашката бавно да се отстранят от гледката.

След осъществяването на тези промени в кода приложението ви трябва да функционира като при запис на екрана (Фигура Z).

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

Рестартиране на играта и запазване на данни с високи резултати

Сега сме изградили работеща игра на змия (в по-голямата си част). Последните стъпки са в полезрението! Нуждаем се от метод за рестартиране на играта и връщане към менюто, също така трябва да запишем данните от високия резултат на устройството, ако резултатът от този кръг е по-добър от най-добрия ви най-добър резултат.

Първо нека да приложим метод, който се връща в менюто, след като змията приключи анимацията си. Добавете следния код към вашия GameManager.swift файл, така че вашият код да съвпада с фигура AA.

// 1
finishAnimation ()
// 2
частен функционален завършекАнимация () {
    ако playerDirection == 0 && scene.playerPositions.count> 0 {
        var hasFinished = true
        нека headOfSnake = scene.playerPositions [0]
        за позиция в scene.playerPositions {
            ако headOfSnake! = позиция {
                hasFinished = false
            }
         }
     ако е завършил {
        печат ("крайна игра")
        playerDirection = 4
        // анимацията е завършена
        scene.scorePos = нула
        scene.playerPositions.removeAll ()
        renderChange ()
        // връщане към менюто
        scene.currentScore.run (SKAction.scale (до: 0, продължителност: 0,4) {
        self.scene.currentScore.isHidden = true
}
        scene.gameBG.run (SKAction.scale (до: 0, продължителност: 0,4)) {
            self.scene.gameBG.isHidden = true
            self.scene.gameLogo.isHidden = false
            self.scene.gameLogo.run (SKAction.move (до: CGPoint (x: 0, y: (self.scene.frame.size.height / 2) - 200), продължителност: 0,5)) {
                 self.scene.playButton.isHidden = false
                 self.scene.playButton.run (SKAction.scale (до: 1, продължителност: 0,3))
                 self.scene.bestScore.run (SKAction.move (до: CGPoint (x: 0, y: self.scene.gameLogo.position.y - 50), продължителност: 0,3))
               }
          }
          }
     }
}
Фигура АА

Ето обяснение за този метод:

  • 1: Обадете се на функцията за финализиране ().
  • 2: Тази функция ще провери завършването на последната анимация на змията, когато се затвори в себе си. След като всички позиции в масива playerPositions съвпадат помежду си, змията се е свила до един квадрат. След като това се случи, ние задаваме playDirection на 4 (преди това беше зададено на 0, показващо смърт) и след това показваме обектите от менюто. Ние също крием текущия етикет наScoreScore и gameBG обекта (решетката на квадратите).

Нека добавим метод, който запазва високия резултат на устройството, така че когато приложението се затвори, да не губим данните си с висок резултат. В новия метод (finishAnimation ()) току-що написахте добавете този ред код, така че файлът ви да съвпада с фигура BB.

updateScore ()
Фигура BB

Сега отворете вашия AppDelegate.swift файл и добавете следните редове код, така че вашият проект да съвпада с фигура CC. Този фрагмент на код използва UserDefaults, за да запише данни в паметта на вашето устройство. Ако планирате да изградите проект с огромно количество съхранени данни, това може да доведе до проблем, но той работи добре за прости неща като превключватели на настройки и променливи.

нека по подразбиране = UserDefaults.standard
нека defaultValue = ["bestScore": 0]
defaults.register (по подразбиране: defaultValue)
Фигура CC

Сега се върнете към вашия GameManager.swift файл и създайте следния метод, така че вашият код да съвпада с Фигура DD. Този блок код просто проверява дали резултатът е победил най-добрия резултат и съответно се актуализира.

// 1
private func updateScore () {
     ако currentScore> UserDefaults.standard.integer (forKey: "bestScore") {
          UserDefaults.standard.set (текущScore, forKey: „най-добър Резултат“)
     }
     currentScore = 0
     scene.currentScore.text = "Резултат: 0"
     scene.bestScore.text = "Най-добър резултат: \ (UserDefaults.standard.integer (forKey:" bestScore "))"
}
Фигура DD

Отворете GameScene.swift и редактирайте функцията си InitializeMenu (), така че файлът ви да съвпада с фигура EE. Това гарантира, че когато играта зарежда запазения най-добър резултат се показва, а не 0.

bestScore.text = "Най-добър резултат: \ (UserDefaults.standard.integer (forKey:" bestScore "))"
Фигура EE

След добавяне на този нов код вашият висок резултат ще бъде запазен в паметта на вашето устройство, след като приложението се затвори.

Затварящи мисли

За да премахнете информацията за разработчика в долната част на екрана, отворете вашия GameViewController.swift файл и задайте view.showFPS и view.showsNodeCount на невярно.

Вече сте изградили цяла iPhone игра от нулата! Ето видео, демонстриращо крайния продукт.

Ако ви хареса проектът, помислете за проверка на приложението ми за IOS! Ако имате някакви проблеми или въпроси, не се колебайте да ми пишете на адрес shrader.gavin@gmail.com.

Това, което изградихме днес, едва надраска повърхността на сложността, която играчният двигател SpriteKit може да предложи. В бъдеще планирам да правя уроци, обхващащи физика, дизайн на нива и анимации.