Mengenal Swift dengan Snake



Halo semuanya! Untuk mengantisipasi peluncuran kursus "iOS-developer. Kursus dasar ” kami menyelenggarakan pelajaran terbuka lainnya. Webinar ini dirancang untuk orang yang memiliki pengalaman dalam pengembangan dalam bahasa dan platform apa pun, tetapi ingin mempelajari bahasa Swift dan menguasai pengembangan untuk iOS. Dalam pelajaran, kami memeriksa secara terperinci sintaks dan konstruksi kunci dari bahasa Swift , berkenalan dengan alat pengembangan utama.



Peserta Webinar belajar:

  • apa bahasa Swift, apa saja fiturnya;
  • Bagaimana lingkungan pengembangan Xcode membantu Anda memulai
  • cara membuat game sederhana untuk iOS.

Webinar itu dilakukan oleh Alexei Sobolevsky , pengembang iOS di Yandex.

Lakukan sendiri, Ular


Untuk bekerja, kami menggunakan Xcode lingkungan pengembangan terintegrasi. Ini adalah lingkungan yang nyaman, gratis, dan fungsional yang dibuat oleh Apple.

Pada awalnya, kami membuat proyek baru dan memilih set dasar file "Game":



Tanpa basa-basi lagi, mereka menyebut proyek "Ular". Semua pengaturan dibiarkan secara default, memastikan bahwa SpriteKit berada di garis Teknologi Game.

Rincian pembuatan proyek.

Setelah melakukan tindakan di atas, daftar file yang dibuat secara otomatis untuk proyek kami akan ditampilkan di bagian kiri jendela. Salah satu file yang paling penting adalah AppDelegate.swift , yang membantu sistem berkomunikasi dengan kode kami ketika ada peristiwa signifikan untuk aplikasi terjadi (peluncuran, tekan, klik tautan, dll.). Kode file ini:

// // AppDelegate.swift // SnakeGame // // Created by Alexey Sobolevsky on 15/09/2019. // Copyright 2019 Alexey Sobolevsky. All rights reserved. // import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } } 

File yang sama pentingnya adalah GameScene.swift dan GameViewController.swift . Kelas GameScene menciptakan adegan, dan GameViewController bertanggung jawab untuk satu layar aplikasi yang kita lihat (satu layar - satu GameViewController). Tentu saja, aturan ini tidak selalu didukung, tetapi secara umum berfungsi. Karena aplikasi kita cukup sederhana, kita hanya akan memiliki satu GameViewController. Mari kita mulai dengannya.

Menulis GameViewController


Kami akan menghapus kode default. Pengontrol tampilan memiliki beberapa metode yang berfungsi tergantung pada kondisi layar. Misalnya, viewDidLoad() dipicu ketika semua elemen layar sudah dimuat, dan layar akan segera muncul di smartphone. Karena kita memiliki permainan, kita harus menempatkan adegan permainan di pengontrol tampilan kita (ini adalah tempat ular akan berjalan dan semua peristiwa lain dari permainan akan terjadi).

Buat heboh:

 let scene = GameScene(size: view.bounds.size) 

let adalah konstanta dan kata kunci. Swift juga menggunakan kata kunci var, yang diperlukan untuk mendefinisikan variabel. Dengan menggunakan var , kita dapat mengubah nilai variabel berkali-kali saat program sedang berjalan. Dengan menggunakan let, kita tidak dapat mengubah nilai variabel setelah inisialisasi.

Sekarang kita perlu memastikan bahwa tampilan tempat kita meletakkan adegan yang dibuat sesuai dengan jenis yang diinginkan. Untuk melakukan ini, gunakan konstruksi pelindung - ini sama seperti if , hanya sebaliknya (jika tidak):

 guard let skView = view as? SKView else { return } 

Setelah memastikan bahwa elemen layar cocok dengan tipe yang diinginkan, kami menambahkan adegan kami ke dalamnya:

 skView.presentScene(scene) 

Anda juga perlu menunjukkan jumlah frame per detik (FPS):

 skView.showsFPS = true 

Kemudian tampilkan jumlah elemen dari semua jenis di tempat kejadian:

  skView.showsNodeCount = true 

Dan mari kita buat elemen muncul di layar terlepas dari urutannya dalam hierarki elemen:

 skView.ignoresSiblingOrder = true 

Dan jangan lupa bahwa adegan kita harus direntangkan ke lebar layar:

 scene.scaleMode = .resizeFill 

Berikut ini adalah kode terakhir untuk file GameViewController.swift :

 import UIKit import SpriteKit import GameplayKit class GameViewController: UIViewController { override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() setup() } private func setup() { guard let skView = view as? SKView else { return } let scene = GameScene(size: view.bounds.size) skView.showsFPS = true skView.showsNodeCount = true skView.ignoresSiblingOrder = true scene.scaleMode = .resizeFill skView.presentScene(scene) } } 

Rincian membuat file GameViewController.swift.

Jadi, kami membuat adegan, tapi itu kosong, jadi jika kami menjalankan emulator sekarang, kami hanya akan melihat layar hitam.

Menulis GameScene


Seperti terakhir kali, kami menghapus sebagian besar kode, dan kemudian melakukan pengaturan yang diperlukan untuk adegan tersebut . Ini juga memiliki metode sendiri. Misalnya, karena kami menambahkan adegan kami ke ViewController, kami memerlukan metode didMove() :

 override func didMove(to view: SKView) { setup(in: view) } 

Lebih jauh, saat game dimulai, metode Update() dipanggil untuk setiap frame:

 override func update(_ currentTime: TimeInterval) { snake?.move() } 

Dan kami juga membutuhkan beberapa penangan untuk mengetuk layar:

 override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touchedNode = findTouchedNode(with: touches) else { return } 

 override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touchedNode = findTouchedNode(with: touches) else { return } 

Seperti yang Anda ketahui, Swift terkenal dengan keberadaan gula sintaksis . Gula sintaksis - ini adalah aspek teknis yang menyederhanakan kehidupan pengembang, mempercepat penulisan kode. Semua ini banyak membantu dalam mengatur adegan, yang akan kita lakukan sekarang. Pertama-tama, atur warnanya:

 backgroundColor = SKColor.white 

Karena ular itu bekerja di pesawat, kita tidak perlu fisika, dan Anda bisa mematikannya agar ular tidak jatuh karena gravitasi. Juga, kita tidak perlu gim berputar, dll .:

 physicsWorld.gravity = .zero physicsWorld.contactDelegate = self physicsBody = SKPhysicsBody(edgeLoopFrom: frame) physicsBody?.allowsRotation = false physicsBody?.categoryBitMask = CollisionCategories.edgeBody physicsBody?.collisionBitMask = CollisionCategories.snake | CollisionCategories.snakeHead view.showsPhysics = true 

Sekarang buat tombol:

 let counterClockwiseButton = ControlsFactory.makeButton(at: CGPoint(x: scene.frame.minX + 30, y: scene.frame.minY + 50), name: .counterClockwiseButtonName) addChild(counterClockwiseButton) let clockwiseButton = ControlsFactory.makeButton(at: CGPoint(x: scene.frame.maxX - 90, y: scene.frame.minY + 50), name: .clockwiseButtonName) addChild(clockwiseButton) 

Ketika Anda telah menulis sepotong kode, Anda harus mempertimbangkan apakah kode tersebut dapat diperbaiki atau dire-refraktekan sehingga dapat digunakan kembali di masa depan. Lihat, pada dasarnya kita memiliki dua tombol di layar, untuk pembuatan yang menggunakan kode yang sama. Jadi, kode ini dapat diambil dalam fungsi yang terpisah. Untuk melakukan ini, buat kelas baru dan, karenanya, file ControlsFactory.swift dengan kode berikut:

 import SpriteKit final class ControlsFactory { static func makeButton(at position: CGPoint, name: String) -> SKShapeNode { let button = SKShapeNode() button.path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 45, height: 45)).cgPath button.position = position button.fillColor = .gray button.strokeColor = UIColor.lightGray.withAlphaComponent(0.7) button.lineWidth = 10 button.name = name return button } } 

Untuk menggambar apel acak yang akan "dimakan" ular kita, buat kelas Apple dan file Apple.swift :

 import SpriteKit final class Apple: SKShapeNode { let diameter: CGFloat = 10 convenience init(at point: CGPoint) { self.init() path = UIBezierPath(ovalIn: CGRect(x: -diameter/2, y: -diameter/2, width: diameter, height: diameter)).cgPath fillColor = .red strokeColor = UIColor.red.withAlphaComponent(0.7) lineWidth = 5 position = point physicsBody = SKPhysicsBody(circleOfRadius: diameter / 2, center: .zero) physicsBody?.categoryBitMask = CollisionCategories.apple } } 

Dan kami menggambarkan apel kami dengan fungsi createApple() di GameScene.swift :

 private func createApple() { let padding: UInt32 = 15 let randX = CGFloat(arc4random_uniform(UInt32(gameFrameRect.maxX) - padding) + padding) let randY = CGFloat(arc4random_uniform(UInt32(gameFrameRect.maxY) - padding) + padding) let apple = Apple(at: CGPoint(x: randX, y: randY).relative(to: gameFrameRect)) gameFrameView.addChild(apple) } 

Nah, giliran telah datang untuk ular itu. Ini akan terdiri dari dua bagian: tubuh ( SnakeBodyPart.swift ) dan kepala ( SnakeHead.swift ).

Kode SnakeBodyPart.swift :

 import SpriteKit class SnakeBodyPart: SKShapeNode { init(at point: CGPoint, diameter: CGFloat = 10.0) { super.init() path = UIBezierPath(ovalIn: CGRect(x: -diameter/2, y: -diameter/2, width: diameter, height: diameter)).cgPath fillColor = .green strokeColor = UIColor.green.withAlphaComponent(0.7) lineWidth = 5 position = point physicsBody = SKPhysicsBody(circleOfRadius: diameter - 4, center: .zero) physicsBody?.isDynamic = true physicsBody?.categoryBitMask = CollisionCategories.snake physicsBody?.contactTestBitMask = CollisionCategories.edgeBody | CollisionCategories.apple } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } 

Kode SnakeHead.swift :

 import SpriteKit final class SnakeHead: SnakeBodyPart { init(at point: CGPoint) { super.init(at: point, diameter: 20) physicsBody?.categoryBitMask = CollisionCategories.snakeHead physicsBody?.contactTestBitMask = CollisionCategories.edgeBody | CollisionCategories.apple } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } 

Namun, kami tidak akan membuat Anda bosan dengan deskripsi setiap baris, karena detail pembuatan file GameScene.swift dan kelas lainnya ditampilkan dengan baik di video. Kami hanya menawarkan untuk melihat kode terakhir dari GameScene.swift :

 import SpriteKit import GameplayKit class GameScene: SKScene { var gameFrameRect: CGRect = .zero var gameFrameView: SKShapeNode! var startButton: SKLabelNode! var stopButton: SKLabelNode! var snake: Snake? override func didMove(to view: SKView) { setup(in: view) } override func update(_ currentTime: TimeInterval) { snake?.move() } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touchedNode = findTouchedNode(with: touches) else { return } if let shapeNode = touchedNode as? SKShapeNode, touchedNode.name == .counterClockwiseButtonName || touchedNode.name == .clockwiseButtonName { shapeNode.fillColor = .green if touchedNode.name == .counterClockwiseButtonName { snake?.moveCounterClockwise() } else if touchedNode.name == .clockwiseButtonName { snake?.moveClockwise() } } else if touchedNode.name == .startButtonName { start() } else if touchedNode.name == .stopButtonName { stop() } } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touchedNode = findTouchedNode(with: touches) else { return } if let shapeNode = touchedNode as? SKShapeNode, touchedNode.name == .counterClockwiseButtonName || touchedNode.name == .clockwiseButtonName { shapeNode.fillColor = .gray } } override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touchedNode = findTouchedNode(with: touches) else { return } if let shapeNode = touchedNode as? SKShapeNode, touchedNode.name == .counterClockwiseButtonName || touchedNode.name == .clockwiseButtonName { shapeNode.fillColor = .gray } } // MARK: - private func start() { guard let scene = scene else { return } snake = Snake(at: CGPoint(x: scene.frame.midX, y: scene.frame.midY)) gameFrameView.addChild(snake!) createApple() startButton.isHidden = true stopButton.isHidden = false } private func stop() { snake = nil gameFrameView.removeAllChildren() startButton.isHidden = false stopButton.isHidden = true } private func setup(in view: SKView) { backgroundColor = SKColor.white physicsWorld.gravity = .zero physicsWorld.contactDelegate = self physicsBody = SKPhysicsBody(edgeLoopFrom: frame) physicsBody?.allowsRotation = false physicsBody?.categoryBitMask = CollisionCategories.edgeBody physicsBody?.collisionBitMask = CollisionCategories.snake | CollisionCategories.snakeHead view.showsPhysics = true let margin: CGFloat = 20 let gameFrame = frame.inset(by: view.safeAreaInsets) gameFrameRect = CGRect(x: margin, y: margin + view.safeAreaInsets.top + 55, width: gameFrame.width - margin * 2, height: gameFrame.height - margin * 2 - 55) drawGameFrame() guard let scene = view.scene else { return } let counterClockwiseButton = ControlsFactory.makeButton(at: CGPoint(x: scene.frame.minX + 30, y: scene.frame.minY + 50), name: .counterClockwiseButtonName) addChild(counterClockwiseButton) let clockwiseButton = ControlsFactory.makeButton(at: CGPoint(x: scene.frame.maxX - 90, y: scene.frame.minY + 50), name: .clockwiseButtonName) addChild(clockwiseButton) startButton = SKLabelNode(text: "START") startButton.position = CGPoint(x: scene.frame.midX, y: 55) startButton.fontSize = 40 startButton.fontColor = .green startButton.name = .startButtonName addChild(startButton) stopButton = SKLabelNode(text: "STOP") stopButton.position = CGPoint(x: scene.frame.midX, y: 55) stopButton.fontSize = 40 stopButton.fontColor = .red stopButton.name = .stopButtonName stopButton.isHidden = true addChild(stopButton) } final func drawGameFrame() { gameFrameView = SKShapeNode(rect: gameFrameRect) gameFrameView.fillColor = .lightGray gameFrameView.lineWidth = 2 gameFrameView.strokeColor = .green addChild(gameFrameView) } private func findTouchedNode(with touches: Set<UITouch>) -> SKNode? { return touches.map { [unowned self] touch in touch.location(in: self) } .map { atPoint($0) } .first } private func createApple() { let padding: UInt32 = 15 let randX = CGFloat(arc4random_uniform(UInt32(gameFrameRect.maxX) - padding) + padding) let randY = CGFloat(arc4random_uniform(UInt32(gameFrameRect.maxY) - padding) + padding) let apple = Apple(at: CGPoint(x: randX, y: randY).relative(to: gameFrameRect)) gameFrameView.addChild(apple) } } // MARK: - SKPhysicsContactDelegate extension GameScene: SKPhysicsContactDelegate { func didBegin(_ contact: SKPhysicsContact) { var contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask contactMask ^= CollisionCategories.snakeHead switch contactMask { case CollisionCategories.apple: let apple = contact.bodyA.node is Apple ? contact.bodyA.node : contact.bodyB.node snake?.addBodyPart() apple?.removeFromParent() createApple() case CollisionCategories.edgeBody: stop() break default: break } } } private extension String { static let counterClockwiseButtonName = "counterClockwiseButton" static let clockwiseButtonName = "clockwiseButton" static let startButtonName = "startButton" static let stopButtonName = "stopButton" } 

Hasilnya adalah game Snake paling sederhana:



Kami butuh sekitar satu setengah jam untuk menulis permainan. Jika Anda ingin mendapatkan keterampilan pemrograman di Swift, ulangi semua langkah sendiri. Omong-omong, di sini Anda akan mendapatkan akses penuh ke semua file kode yang digunakan dalam proyek ini.

Source: https://habr.com/ru/post/id469537/


All Articles