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.