Use swift and spritekit to write a ninja game

Source: Internet
Author: User
Tags addchild spritekit


The game in this article is completed using spritekit and swift language.

Spritekit is Apple's own game engine, which is better suited to the underlying API of the IOS system. However, its architecture and implementation are similar to cocos2d. So the usage is actually not much different, but spritekit is more lightweight.


Program entry
Like oC, the main function points the entry to appdelegate. The cocoa touch framework is almost the same as OC, but it is rewritten using SWIFT.


The built-in methods of these templates are no different from those of the OC project...
Start programming games
If you know about ccnode, ccsprite, and ccscene, it seems that spritekit has almost no problems.
    override func viewWillLayoutSubviews() {        super.viewWillLayoutSubviews()                var skView : SKView = self.view as SKView        if !skView.scene {            //DEBUG            skView.showsFPS = true            skView.showsNodeCount = true                        var scene : SKScene = GameScene.sceneWithSize(skView.bounds.size)            scene.scaleMode = .AspectFill                        skView.presentScene(scene)        }    }


When the viewdidload method is called, The skview is not added to the view hierarchy, so it cannot change the corresponding direction or layout. Therefore, the bounds attribute of skview is not the correct value after the horizontal screen, but the value corresponding to the default vertical screen. It seems that this is not a good time to initialize scene. So we need to move this part of code to the method to deploy the sub-view.

Play background music
Here we use avaudioplayer to play music. In controller, declare an attribute var backgroundmusicplayer: avaudioplayer?

func setupMedia() {                var error : NSError?        let backgroundMusicURL : NSURL = NSBundle.mainBundle().URLForResource(BG_MUSIC_NAME, withExtension: "caf")        backgroundMusicPlayer = AVAudioPlayer(contentsOfURL: backgroundMusicURL , error: &error)        if error {            println("load background music error : \(error)")        } else {            backgroundMusicPlayer!.numberOfLoops = -1            backgroundMusicPlayer!.prepareToPlay()            backgroundMusicPlayer!.play()        }    }

    override func viewDidLoad() {        super.viewDidLoad()        setupMedia()    }

Start playing when the view is loaded.

Game scenarios
We have created a skscene subclass to compile game display and logic. Class gamescene: skscene

Victory failure scenario
class GameOverScene : SKScene {        convenience init(size: CGSize, won: Bool) {        self.init(size: size)        self.backgroundColor = SKColor(red:1.0, green:1.0, blue:1.0, alpha:1.0)                self.setupMsgLabel(isWon :won)        self.directorAction()    }        func setupMsgLabel(isWon won: Bool) {        var msg: String = won ? "Yow Won!" : "You Lose :["                var msgLabel = SKLabelNode(fontNamed: "Chalkduster")        msgLabel.text = msg        msgLabel.fontSize = 40        msgLabel.fontColor = SKColor.blackColor()        msgLabel.position = CGPointMake(self.size.width/2, self.size.height/2)        self.addChild(msgLabel)    }        func directorAction() {        var actions: AnyObject[] = [ SKAction.waitForDuration(3.0), SKAction.runBlock({            var reveal = SKTransition.flipHorizontalWithDuration(0.5)            var gameScene = GameScene(size: self.size)            self.view.presentScene(gameScene, transition: reveal)            }) ]        var sequence = SKAction.sequence(actions)                self.runAction(sequence)    }    }

A simple page that shows the game's victory and failure, with only one label and some actions.
Initialization
VaR PLAYER: skspritenode! // Hero genie var lastspawntimeinterval: nstimeinterval! // Record the last time and update time var lastupdatetimeinterval: nstimeinterval! VaR monstersdestroyed: int! // Record the number of destroyed monsters


    init(size: CGSize) {        super.init(size: size)                self.backgroundColor = SKColor(red: 1.0, green:1.0, blue:1.0, alpha:1.0)        player = SKSpriteNode(imageNamed: "player")        player.position = CGPointMake(self.player.size.width/2, self.frame.size.height/2)        self.addChild(player)                monstersDestroyed = 0        lastSpawnTimeInterval = 0        lastUpdateTimeInterval = 0                gameLevel.nextLevel()                //physics        self.physicsWorld.gravity = CGVectorMake(0, 0)        self.physicsWorld.contactDelegate = self    }


Declared some attributes and assigned values during the construction process. The hero genie is instantiated. Basic physical engine attributes are set.

Add monsters

func addMonster() {        var monster = SKSpriteNode(imageNamed: "monster")                //location        var minY = monster.size.height/2        var maxY = self.frame.size.height - monster.size.height/2        var rangeY = maxY - minY        var actualY = arc4random() % rangeY + minY                monster.position = CGPointMake(self.frame.size.width + monster.size.width/2, actualY)        self.addChild(monster)                //physics        monster.physicsBody = SKPhysicsBody(rectangleOfSize: monster.size)        monster.physicsBody.dynamic = true        monster.physicsBody.categoryBitMask = monsterCategory        monster.physicsBody.contactTestBitMask = projectileCategory        monster.physicsBody.collisionBitMask = 0                //speed        var minDuration = 2.0        var maxDuration = 4.0        var rangeDuration = maxDuration - minDuration        var actualDuration = arc4random() % rangeDuration + minDuration                var actionMove = SKAction.moveTo(CGPointMake(-monster.size.width/2, actualY), duration: actualDuration)        var actionMoveDone = SKAction.removeFromParent()        var loseAction = SKAction.runBlock({            var reveal = SKTransition.flipHorizontalWithDuration(0.5)            var gameOverScene = GameOverScene(size: self.size, won: false)            self.view.presentScene(gameOverScene, transition: reveal)            })                monster.runAction(SKAction.sequence([actionMove, loseAction, actionMoveDone]))    }

The monsters are initialized, physically configured, speed set, and moved. If the left boundary is exceeded, the game is regarded as a failure. If the ninja dart is thrown in the middle, it will be destroyed, this part is implemented by collision detection, which will be mentioned later.

Add darts
When we click the screen, we need to launch a dart to launch the attack.
The system has its own listening method, which is the same as that in uikit.
override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) {        // get touch        var touch = touches.anyObject() as UITouch        var location = touch.locationInNode(self)                //bullet action        self.addProjectile(location: location)    }

Then the method for adding a bullet
    func addProjectile(#location: CGPoint) {        var projectile = SKSpriteNode(imageNamed:"projectile")        projectile.position = player.position                //physics        projectile.physicsBody = SKPhysicsBody(circleOfRadius: projectile.size.width/2)        projectile.physicsBody.dynamic = true        projectile.physicsBody.categoryBitMask = projectileCategory        projectile.physicsBody.contactTestBitMask = monsterCategory        projectile.physicsBody.collisionBitMask = 0        projectile.physicsBody.usesPreciseCollisionDetection = true                var offset = niSub(location, projectile.position)        if offset.x < 0 {return}                self.addChild(projectile)                // direct unit vector        var direction = niNormalize(offset)        //to screen's edge        var shootAmount = niMult(direction, 1000)        //now loc        var realDest = niAdd(shootAmount, projectile.position)                //action        var velocity = 480.0/1.0        var realMoveDuration = Double(self.size.width) / velocity                var actionMove = SKAction.moveTo(realDest, duration: realMoveDuration)        var actionMoveDone = SKAction.removeFromParent()        var sequence = SKAction.sequence([actionMove, actionMoveDone])        projectile.runAction(sequence)                self.runAction(SKAction.playSoundFileNamed("pew-pew-lei.caf", waitForCompletion: false))    }

Like a monster, we initialized the dart and configured its physical status. Then, we determined its vector direction based on the clicked position and the hero's position so that he could start moving. Then let him move in that direction.
Some custom closure functions are used for game assistance in determining the direction of movement. Because SWIFT is a type-safe language, we often cannot directly perform operations on different types of values, as in C ++, Swift can also perform Operator overloading.
// overload@infix func %(lhs: UInt32, rhs: Float) -> Float {    return Float(lhs) % Float(rhs)}@infix func %(lhs: UInt32, rhs: Double) -> Double {    return Double(lhs) % Double(rhs)}let niAdd = {(a: CGPoint, b: CGPoint) -> CGPoint in CGPointMake(a.x + b.x, a.y + b.y)}let niSub = {(a: CGPoint, b: CGPoint) -> CGPoint in CGPointMake(a.x - b.x, a.y - b.y)}let niMult = {(a: CGPoint, b: Float) -> CGPoint in CGPointMake(a.x * b, a.y * b)}let niLength = {(a: CGPoint) -> CGFloat in CGFloat(sqrt(Double(a.x * a.x + a.y * a.y)))}
// unit vectorlet niNormalize = {(a : CGPoint) -> CGPoint in    var length = niLength(a)    return CGPointMake(a.x / length, a.y / length)}


Suitable time to add monsters
We have not called the method of adding monsters before. In iOS, the number of frames per second is 60, while in skscene, refresh frames will use the default update method to write game logic.
override func update(currentTime: NSTimeInterval) {        var timeSinceLast: CFTimeInterval = currentTime - lastSpawnTimeInterval        lastUpdateTimeInterval = currentTime        if timeSinceLast > 1 {            timeSinceLast = Double(gameLevel.toRaw()) / 60.0            lastUpdateTimeInterval = currentTime        }                self.updateWithTimeSinceLastUpdate(timeSinceLast: timeSinceLast)    }

Now we can add monsters.
    func updateWithTimeSinceLastUpdate(#timeSinceLast: CFTimeInterval) {        lastSpawnTimeInterval = lastSpawnTimeInterval + timeSinceLast        if lastSpawnTimeInterval > 1 {            lastSpawnTimeInterval = 0            self.addMonster()        }    }

Collision Detection
Finally, we need to define the collision logic.
Proxy method callback is triggered when the physical model is connected.
    func didBeginContact(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 & projectileCategory) != 0 && (secondBody.categoryBitMask & monsterCategory) != 0 {            self.didCollide(projectile: firstBody.node as SKSpriteNode, monster: secondBody.node as SKSpriteNode)        }    }

At this time, we hope that the following logic will be implemented when the monsters and darts collide.
    func didCollide(#projectile: SKSpriteNode, monster: SKSpriteNode) {        projectile.removeFromParent()        monster.removeFromParent()                monstersDestroyed = monstersDestroyed + 1        if monstersDestroyed > 30 {            var reveal = SKTransition.flipHorizontalWithDuration(0.5)            var gameOverScene = GameOverScene(size: self.size, won: true)            self.view.presentScene(gameOverScene, transition: reveal)        }    }

In this way, the entire game of ninja darts and monsters is complete.
The following is the game:




Game Code: Click to open the link

The above is all the content of this blog. Welcome and discuss.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.