Arabic (العربية/عربي) translation by Aisy Abdullah (you can also view the original English article)



في هذا المنصب سنقوم ببناء لعبة بسيطة من نقطة الصفر. على طول الطريق، أننا سوف أتطرق إلى بعض الجوانب الأكثر أهمية للمكتبة سبريتيكيت.
ويستند هذا المنصب ما تعلمناه سابقا في سلسلة 'أساسيات سبريتيكيت'. إذا كنت ترغب في تحديث معرفتك سبريتيكيت، نلقي نظرة على بعض من بلدي وظائف أخرى.
- SpriteKitعرض سبريتيكيتجيمس Tyner
- سبريتيكيتأساسيات سبريتيكيت: العقدجيمس Tyner
- iOS SDKأساسيات سبريتيكيت: العفاريتجيمس Tyner
- سبريتيكيتأساسيات SpriteKit: الإجراءات والفيزياءجيمس Tyner
مشروع جديد
افتح Xcode وابدأ مشروعًا جديدًا من القائمة File> New> Project. تأكد من اختيار نظام iOS واختيار اللعبة كقالب لك.



أعط مشروعك اسمًا ، وتأكد من تعيين اللغة على Swift ، تم تعيين تقنية الألعاب على SpriteKit ، وتم تعيين الأجهزة على iPad.



تخطيط مشاهد اللعبة
من أول الأشياء التي أحب القيام بها عند إنشاء مشروع هو تحديد عدد المشاهد التي سأحتاجها للمشروع. عادةً ما يكون لديّ ثلاثة مشاهد على الأقل: مشهد مقدمة ، ومشهد رئيسي للعبة ، ومشهد لإظهار درجات عالية ، إلخ.
في هذا المثال ، نحتاج فقط إلى مقدمة ومشهد أساسي في اللعب نظرًا لأننا لن نحافظ على تتبع الأرواح والعشرات وما إلى ذلك. إن SpriteKit تأتي بالفعل مع مشهد واحد عند إنشاء مشروع جديد ، لذا فنحن نحتاج فقط إلى مشهد تمهيدى.
من قائمة Xcode ، اختر File> New> File. تأكد من اختيار iOS ، واختر Cocoa Touch Class.



اسم الفئة StartGameScene ، وتأكد من أن فئة فرعية تم تعيينها إلى SKScene وتعيين اللغة إلى Swift.



إعداد GameViewController
افتح GameViewController.swift. احذف كل شيء في هذا الملف واستبدله بما يلي.
import UIKit import SpriteKit import GameplayKit class GameViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let scene = StartGameScene(size: view.bounds.size) let skView = self.view as! SKView skView.showsFPS = false skView.showsNodeCount = false skView.ignoresSiblingOrder = false scene.scaleMode = .aspectFill skView.presentScene(scene) } override var prefersStatusBarHidden: Bool { return true } }
عند إنشاء مشروع جديد ، تم إعداد GameViewController.swift لتحميل GameScene.sks من القرص. يستخدم GameScene.sks جنبا إلى جنب مع محرر المشهد المدمج في SpriteKit ، والذي يسمح لك بصريا وضع المشاريع الخاصة بك. لن نستخدم GameScene.sks ، وسنعمل بدلاً من ذلك على إنشاء كل شيء من الشفرة ، لذلك نبدأ هنا بمثيل جديد من StartGameScene ونقدمه.
إنشاء مقدمة المسرح
أضف التالي إلى StartGameScene.swift الذي تم إنشاؤه حديثًا.
import UIKit import SpriteKit class StartGameScene: SKScene { override func didMove(to view: SKView){ scene?.backgroundColor = .blue let logo = SKSpriteNode(imageNamed: "bigplane") logo.position = CGPoint(x: size.width/2, y: size.height/2) addChild(logo) let newGameBtn = SKSpriteNode(imageNamed: "newgamebutton") newGameBtn.position = CGPoint(x: size.width/2, y: size.height/2 - 350) newGameBtn.name = "newgame" addChild(newGameBtn) } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else { return } let touchLocation = touch.location(in: self) let touchedNode = self.atPoint(touchLocation) if(touchedNode.name == "newgame"){ let newScene = GameScene(size: size) newScene.scaleMode = scaleMode view?.presentScene(newScene) } }}
هذا المشهد بسيط جدا. في طريقة didMove
، نقوم بإضافة شعار وزر. ثم ، في touchesBegan
، نكتشف اللمسات على زر اللعبة الجديدة ونستجيب عن طريق تحميل المشهد الرئيسي GameScene
.
تخطيط فصول اللعبة
الشيء التالي الذي أحب القيام به عند إنشاء لعبة جديدة هو تحديد الفئات التي سأحتاج إليها. أستطيع أن أخبرك على الفور أنني سأحتاج إلى فصل لاعب
وفئة العدو
. كل من هذه الفئات سيمدد SKSpriteNode
. أعتقد أنه بالنسبة لهذا المشروع ، سنقوم فقط بإنشاء اللاعب ورصاص العدو من داخل صفوف كل منهما. يمكن أن تجعل رصاصة منفصلة لاعب وفئات رصاصة العدو إذا كنت تفضل ذلك، واقترح عليك محاولة للقيام بذلك كممارسة الخاص بك.
وأخيرا ، هناك الجزر. هذه لا تملك أي وظيفة محددة ولكن للانتقال إلى أسفل الشاشة. في هذه الحالة ، نظرًا لأنهم مجرد ديكورات ، أعتقد أنه من الأفضل أيضًا عدم إنشاء فصل دراسي ، وبدلاً من ذلك فقط أنشئها في GameScene
الرئيسي.
إنشاء فئة لاعب
من قائمة Xcode ، اختر File> New> File. تأكد من اختيار نظام iOS واختيار Cocoa Touch Class.



تأكد من أنه تم تعيين Class على Player ، تم تعيين فئة فرعية من: إلى SKSpriteNode ، وتم ضبط اللغة على Swift.



الآن إضافة ما يلي إلى Player.swift.
import UIKit import SpriteKit class Player: SKSpriteNode { private var canFire = true private var invincible = false private var lives:Int = 3 { didSet { if(lives < 0){ kill() }else{ respawn() } } } init() { let texture = SKTexture(imageNamed: "player") super.init(texture: texture, color: .clear, size: texture.size()) self.physicsBody = SKPhysicsBody(texture: self.texture!,size:self.size) self.physicsBody?.isDynamic = true self.physicsBody?.categoryBitMask = PhysicsCategories.Player self.physicsBody?.contactTestBitMask = PhysicsCategories.Enemy | PhysicsCategories.EnemyBullet self.physicsBody?.collisionBitMask = PhysicsCategories.EdgeBody self.physicsBody?.allowsRotation = false generateBullets() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } func die (){ if(invincible == false){ lives -= 1 } } func kill(){ let newScene = StartGameScene(size: self.scene!.size) newScene.scaleMode = self.scene!.scaleMode let doorsClose = SKTransition.doorsCloseVertical(withDuration: 2.0) self.scene!.view?.presentScene(newScene, transition: doorsClose) } func respawn(){ invincible = true let fadeOutAction = SKAction.fadeOut(withDuration: 0.4) let fadeInAction = SKAction.fadeIn(withDuration: 0.4) let fadeOutIn = SKAction.sequence([fadeOutAction,fadeInAction]) let fadeOutInAction = SKAction.repeat(fadeOutIn, count: 5) let setInvicibleFalse = SKAction.run { self.invincible = false } run(SKAction.sequence([fadeOutInAction,setInvicibleFalse])) } func generateBullets(){ let fireBulletAction = SKAction.run{ [weak self] in self?.fireBullet() } let waitToFire = SKAction.wait(forDuration: 0.8) let fireBulletSequence = SKAction.sequence([fireBulletAction,waitToFire]) let fire = SKAction.repeatForever(fireBulletSequence) run(fire) } func fireBullet(){ let bullet = SKSpriteNode(imageNamed: "bullet") bullet.position.x = self.position.x bullet.position.y = self.position.y + self.size.height/2 bullet.physicsBody = SKPhysicsBody(rectangleOf: bullet.size) bullet.physicsBody?.categoryBitMask = PhysicsCategories.PlayerBullet bullet.physicsBody?.allowsRotation = false scene?.addChild(bullet) let moveBulletAction = SKAction.move(to: CGPoint(x:self.position.x,y:(scene?.size.height)! + bullet.size.height), duration: 1.0) let removeBulletAction = SKAction.removeFromParent() bullet.run(SKAction.sequence([moveBulletAction,removeBulletAction])) } }
ضمن طريقة init ()
، قمنا بإعداد physicsBody
واستدعاء generateBullets ()
. يقوم الأسلوب generateBullets
بشكل متكرر باستدعاء fireBullet ()
، الذي يقوم بإنشاء رمز نقطي ، ويقوم بتعيين physicsBody
، ويقوم بنقله إلى أسفل الشاشة.
عندما يفقد اللاعب حياة، يتم استدعاء الأسلوب respawn()
. داخل الأسلوب بيضة
، نحن تتلاشى الطائرة والخروج خمس مرات، خلال الوقت الذي سيكون اللاعب الذي لا يقهر. اللاعب واحد قد استنفد كل حياة، يتم استدعاء الأسلوب kill()
. ببساطة تحميل طريقة قتل في StartGameScene
.
إنشاء فئة العدو
اختر ملف> جديد> ملف من قائمة Xcode. تأكد من اختيار نظام iOS واختيار Cocoa Touch Class.



تأكد من أن يتم تعيين فئة إلى العدو، فئة فرعية من: تعيين إلى سكسبريتينودي، ويتم تعيين اللغة إلى سويفت.



يضاف ما يلي إلى Enemy.swift.
import UIKit import SpriteKit class Enemy: SKSpriteNode { init() { let texture = SKTexture(imageNamed: "enemy1") super.init(texture: texture, color: .clear, size: texture.size()) self.name = "enemy" self.physicsBody = SKPhysicsBody(texture: self.texture!, size: self.size) self.physicsBody?.isDynamic = true self.physicsBody?.categoryBitMask = PhysicsCategories.Enemy self.physicsBody?.contactTestBitMask = PhysicsCategories.Player | PhysicsCategories.PlayerBullet self.physicsBody?.allowsRotation = false move() generateBullets() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } func fireBullet(){ let bullet = SKSpriteNode(imageNamed: "bullet") bullet.position.x = self.position.x bullet.position.y = self.position.y - bullet.size.height * 2 bullet.physicsBody = SKPhysicsBody(rectangleOf: bullet.size) bullet.physicsBody?.categoryBitMask = PhysicsCategories.EnemyBullet bullet.physicsBody?.allowsRotation = false scene?.addChild(bullet) let moveBulletAction = SKAction.move(to: CGPoint(x:self.position.x,y: 0 - bullet.size.height), duration: 2.0) let removeBulletAction = SKAction.removeFromParent() bullet.run(SKAction.sequence([moveBulletAction,removeBulletAction]) ) } func move(){ let moveEnemyAction = SKAction.moveTo(y: 0 - self.size.height, duration: 12.0) let removeEnemyAction = SKAction.removeFromParent() let moveEnemySequence = SKAction.sequence([moveEnemyAction, removeEnemyAction]) run(moveEnemySequence) } func generateBullets(){ let fireBulletAction = SKAction.run{ [weak self] in self?.fireBullet() } let waitToFire = SKAction.wait(forDuration: 1.5) let fireBulletSequence = SKAction.sequence([fireBulletAction,waitToFire]) let fire = SKAction.repeatForever(fireBulletSequence) run(fire) } }
هذه الفئة تشبه إلى حد كبير فئة Player
. قمنا بتعيين phisicsBody
واستدعاء generateBullets ()
. تتحرك ()
ببساطة تحريك العدو إلى أسفل الشاشة.
إنشاء لعبة المشهد الرئيسي
حذف كل شيء داخل GameScene.swift وإضافة ما يلي.
import SpriteKit import GameplayKit import CoreMotion class GameScene: SKScene, SKPhysicsContactDelegate { let player = Player() let motionManager = CMMotionManager() var accelerationX: CGFloat = 0.0 override func didMove(to view: SKView) { physicsWorld.gravity = CGVector(dx:0.0, dy:0.0) self.physicsWorld.contactDelegate = self scene?.backgroundColor = .blue physicsBody = SKPhysicsBody(edgeLoopFrom: frame) physicsBody?.categoryBitMask = PhysicsCategories.EdgeBody player.position = CGPoint(x: size.width/2, y: player.size.height) addChild(player) setupAccelerometer() addEnemies() generateIslands() } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { } func addEnemies(){ let generateEnemyAction = SKAction.run{ [weak self] in self?.generateEnemy() } let waitToGenerateEnemy = SKAction.wait(forDuration: 3.0) let generateEnemySequence = SKAction.sequence([generateEnemyAction,waitToGenerateEnemy]) run(SKAction.repeatForever(generateEnemySequence)) } func generateEnemy(){ let enemy = Enemy() addChild(enemy) enemy.position = CGPoint(x: CGFloat(arc4random_uniform(UInt32(size.width - enemy.size.width))), y: size.height - enemy.size.height) } func didBegin(_ contact: SKPhysicsContact) { var firstBody: SKPhysicsBody var secondBody: SKPhysicsBody if(contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask){ firstBody = contact.bodyA secondBody = contact.bodyB }else{ firstBody = contact.bodyB secondBody = contact.bodyA } if((firstBody.categoryBitMask & PhysicsCategories.Player != 0) && (secondBody.categoryBitMask & PhysicsCategories.Enemy != 0)){ player.die() secondBody.node?.removeFromParent() createExplosion(position: player.position) } if((firstBody.categoryBitMask & PhysicsCategories.Player != 0) && (secondBody.categoryBitMask & PhysicsCategories.EnemyBullet != 0)){ player.die() secondBody.node?.removeFromParent() } if((firstBody.categoryBitMask & PhysicsCategories.Enemy != 0) && (secondBody.categoryBitMask & PhysicsCategories.PlayerBullet != 0)){ if(firstBody.node != nil){ createExplosion(position: (firstBody.node?.position)!) } firstBody.node?.removeFromParent() secondBody.node?.removeFromParent() } } func createExplosion(position: CGPoint){ let explosion = SKSpriteNode(imageNamed: "explosion1") explosion.position = position addChild(explosion) var explosionTextures:[SKTexture] = [] for i in 1...6 { explosionTextures.append(SKTexture(imageNamed: "explosion\(i)")) } let explosionAnimation = SKAction.animate(with: explosionTextures, timePerFrame: 0.3) explosion.run(SKAction.sequence([explosionAnimation, SKAction.removeFromParent()])) } func createIsland() { let island = SKSpriteNode(imageNamed: "island1") island.position = CGPoint(x: CGFloat(arc4random_uniform(UInt32(size.width - island.size.width))), y: size.height - island.size.height - 50) island.zPosition = -1 addChild(island) let moveAction = SKAction.moveTo(y: 0 - island.size.height, duration: 15) island.run(SKAction.sequence([moveAction, SKAction.removeFromParent()])) } func generateIslands(){ let generateIslandAction = SKAction.run { [weak self] in self?.createIsland() } let waitToGenerateIslandAction = SKAction.wait(forDuration: 9) run(SKAction.repeatForever(SKAction.sequence([generateIslandAction, waitToGenerateIslandAction]))) } func setupAccelerometer(){ motionManager.accelerometerUpdateInterval = 0.2 motionManager.startAccelerometerUpdates(to: OperationQueue(), withHandler: { accelerometerData, error in guard let accelerometerData = accelerometerData else { return } let acceleration = accelerometerData.acceleration self.accelerationX = CGFloat(acceleration.x) }) } override func didSimulatePhysics() { player.physicsBody?.velocity = CGVector(dx: accelerationX * 600, dy: 0) } }
نحن نخلق مثيل لاعب
ومثيل CMMotionManager
. نحن نستخدم مقياس التسارع لنقل اللاعب في هذه اللعبة.
ضمن طريقة (:didMove (to
نوقف الجاذبية ، نضبط contactDelegate
، نضيف حلقة حافة ، وضبط وضع اللاعب
قبل إضافته إلى المشهد. ثم نستدعي setupAccelerometer ()
، الذي يقوم بإعداد مقياس التسارع ، ونستدعي أساليب addEnemies ()
و generateIslands ()
.
الأسلوب addEnemies()
مرارا وتكرارا استدعاء الأسلوب generateEnemy()
الذي سيتم إنشاء مثيل من العدو
وإضافته إلى موقع الحادث.
يعمل أسلوب generateIslands ()
بشكل مشابه للأسلوب addEnemies ()
في أنه بشكل متكرر يدعو createIsland ()
الذي ينشئ SKSpriteNode
ويضيفه إلى المشهد. ضمن createIsland ()
، نقوم أيضًا بإنشاء SKAction
الذي يحرك الجزيرة إلى أسفل المشهد.
داخل الأسلوب didBegin(_:)
، ونحن تحقق لمعرفة أي العقد تجعل الاتصال والرد بإزالة العقدة المناسبة من مكان الحادث واستدعاء player.die()
إذا لزم الأمر. ينشئ الأسلوب createExplosion ()
حركة انفجارية ويضيفها إلى المشهد. بمجرد الانتهاء من الانفجار ، يتم إزالته من المشهد.
استنتاج
خلال هذه السلسلة ، تعلمنا بعضًا من أهم المفاهيم المستخدمة في جميع ألعاب SpriteKit تقريبًا. لقد أنهينا هذه السلسلة بإظهار مدى بساطة الحصول على اللعبة الأساسية وتشغيلها. لا تزال هناك بعض التحسينات التي يمكن إجراؤها ، مثل HUB ، درجات عالية ، والأصوات (قمت بتضمين اثنين من ملفات MP3 التي يمكنك استخدامها لهذا في الريبو). آمل أن تكون قد تعلمت شيئًا مفيدًا خلال هذه السلسلة ، وشكرًا على القراءة!
إذا كنت تريد معرفة المزيد حول برمجة الألعاب باستخدام SpriteKit ، فراجع أحد دورات الفيديو الشاملة! سوف تتعلم كيفية بناء لعبة SpriteKit من الألف إلى الياء.