How to make a runaway game with the Sprite Kit and Swift-Part II

Source: Internet
Author: User
Tags addchild rand touch

Original: How to make a Breakout Game with SpriteKit and Swift:part 2
Author: Michael Briscoe
Translator: Kmyhy

Update Description: This tutorial was upgraded by Michael Briscoe to Xcode 8 and Swift 3. The original author is Barbara Reichart.

Welcome back to this tutorial.

In the first part, you create a moving plank and ball into the game.

In the second part, you will add some bricks and other game logic to the game.

This section continues from the first part of the tutorial. If you do not complete the first part, you can download the sample project from here and continue. Bamboo Brick

You've got the ball bouncing around and being able to create collisions, and then add some bamboo bricks to crush. After all, this is a runaway game.

Back to Gamescene.swift, in Didmove (to:) Add bricks to the method:

1 Let
numberofblocks = 8 let
blockwidth = Skspritenode (imagenamed: "block"). Size.width
Let Totalblockswidth = Blockwidth * CGFloat (numberofblocks)
//2 let
xoffset = (frame.width-totalblockswidth)/2
  //3 for
I in 0..<numberofblocks {let
  block = Skspritenode (imagenamed: "Block.png")
  block.position = CG Point (X:xoffset + cgfloat (cgfloat (i) + 0.5) * Blockwidth, 
    y:frame.height * 0.8)

  block.physicsbody = Skphysicsbo DY (rectangleOf:block.frame.size)
  block.physicsbody!. Allowsrotation = False
  block.physicsbody!. friction = 0.0
  block.physicsbody!. Affectedbygravity = False
  block.physicsbody!. IsDynamic = False
  Block.name = Blockcategoryname
  block.physicsbody!. Categorybitmask = blockcategory
  block.zposition = 2
  addChild (block)
}

This code creates 8 bricks and puts them in the center of the screen. Some constants, such as the number of bricks and their width. Calculates the X offset. This is the distance between the first tile and the left edge of the screen. Use the screen width minus the total width of 8 bricks and divide by 2. Create bricks, set the physical properties of each brick, and set the location of each brick according to Blockwidth and Xoffset.

Build & Run to see the effect.

The bricks are ready. But in order to listen to the collision between the ball and the brick, you must modify the contacttestbitmask of the small ball. Still in Gamescene.swift, in Didmove (to:) Add a new category to the method:

ball.physicsbody!. Contacttestbitmask = Bottomcategory | Blockcategory

This sentence uses an OR operator in the middle of bottomcategory and blockcategory. This causes the corresponding bits in the two category to be set to 1 while the other bits are set to 0. Now, the ball and the floor, brick collision will notify the delegate object. Break the Bamboo brick

You have been able to detect the collision between the ball and the bricks, then add an assistant method for Gamescene.swift to remove the bricks from the scene:

Func Breakblock (node:sknode) {let
  particles = Skemitternode (filenamed: "Brokenplatform")!
  Particles.position = node.position
  particles.zposition = 3
  addChild (particles)
  Particles.run ( Skaction.sequence ([Skaction.wait (forduration:1.0), 
    skaction.removefromparent ()]))
  Node.removefromparent ()
}

This method has a sknode parameter. First, it creates a Skemitternode instance with Brokenplatform.sks and sets its location to the location of the node. The zposition of the emitter node is 3, so the particles are displayed on top of other bricks. When the particle emitter is added to the scene, node (bamboo brick) is removed.

Note: The emitter node is a special node that displays the particle effects created with the scene editor. To see what it looks like, open brokenplatform.sks, the particle system I created specifically for this tutorial. For more information on particle systems, please read our 2D IOS & TvOS Game tutorial, which is detailed in this section.

The next thing to do is to handle the delegate notification. In Didbegin (_:) Method is added last:

if Firstbody.categorybitmask = = Ballcategory && Secondbody.categorybitmask = = blockcategory {
  BreakBlock ( node:secondbody.node!)
  Todo:check If the game has been won
}

This code checks whether the ball and brick collide. If this happens, pass node to the Breakblock (node:) method so that the bamboo tile is removed from the screen and the particle effect is displayed.

Build & Run. When the ball hits the bamboo brick, the bamboo brick will be torn apart.

Add Gameplay

Now that all the game elements are ready, it's time to let the player experience the feeling of winning and losing. Understanding State Machines

Most of the game logic is controlled by the game state. For example, if the game is in the "main Menu" state, the player cannot move and the player can move if the game is on play.

A lot of simple games manage game state by using Boolean values in the update loop. With state machines, you can better organize your code when the game becomes complex.

A state machine manages a set of States through a single current state and a series of transition rules between States. When the game state changes, the status opportunity executes the Exit method of the previous state and the entry method of the next state. These methods are used to control gameplay in each state. When the state successfully changes, the state opportunity executes the update loop of the current state.

Apple has introduced the Gameplaykit framework from IOS 9, which has built-in state machine support to make our work easier. Gameplaykit is not the scope of this tutorial, but for now, you will use two of these classes: the Gkstatemachine and the Gkstate classes. Add State

This game has 3 states: Waitingfortap: The game has been loaded, waiting for the player to play. Playing: The game is playing in the process. Gameover: The game is over, either win or lose.

To save time, these 3 states have been added to the project (you can view the Game states filegroup). To create a state machine, first add the necessary import statements to Gamescene.swift:

Import Gameplaykit

Then, declare the variable in Isfingeronpaddle = False:

Lazy var gamestate:gkstatemachine = Gkstatemachine (states: [
  Waitingfortap (scene:self),
  Playing (scene:self) ,
  Gameover (scene:self)])

By defining this variable, you create a state machine for the game. Note that an array of gkstate is used when creating gkstatemachine. Wait for click Status: Waitingfortap

The WAITINGFORTAP state is the state in which the game just loads wait to start. The player will see a tap to play prompt, and the game waits for a touch event to enter the play state as soon as it occurs.

In Didmove (to:) Add code to the method:

Let Gamemessage = Skspritenode (imagenamed: "Taptoplay")
gamemessage.name = Gamemessagename
Gamemessage.position = Cgpoint (X:frame.midx, y:frame.midy)
gamemessage.zposition = 4
Gamemessage.setscale ( 0.0)
AddChild (gamemessage)

gamestate.enter (waitingfortap.self)

This creates a sprite to display the Tap to Play text, followed by the Game over. Then tell the state machine to enter the WAITINGFORTAP state.

At the same time in Didmove (to:) method, delete this sentence:

ball.physicsbody!. Applyimpulse (Cgvector (dx:2.0, dy: -2.0))//REMOVE

You'll have to move this sentence to get to the play state.

Open the Waitingfortap.swift file under the Game States folder. The Didenter (from:) and Willexit (to:) Method is modified to:

Override Func Didenter (from Previousstate:gkstate?) {Let scale
  = Skaction.scale (to:1.0, duration:0.25)
  scene.childnode (withname:gamemessagename)!. Run (scale)
} The

override Func Willexit (to nextstate:gkstate) {
  If nextstate are Playing {let scale
    = Skactio N.scale (to:0, duration:0.4)
    scene.childnode (withname:gamemessagename)!. Run (scale)
  }
}

Didenter (from:) When the game enters Waitingfortap state method is called. This method simply enlarges the Tap to Play and tells the player to start.

When the game exits the Waitingfortap state and enters the play state, Willexit (to:) method is called, Tap to Play is reduced to 0.

Build & Run, tap the screen to start playing the game.

OK, but when you click on the screen, nothing happens. That's the next game state thing. "In-game" status

The playging state will start the game and manage the speed of the ball.

First, go back to Gamescene.swift and implement the helper method:

Func randomfloat (From:cgfloat, to:cgfloat), cgfloat {let
  rand:cgfloat = CGFloat (Float (Arc4random ())/0xFFFFF FFF)
  return (RAND) * (To-from) + from
}

This helper method returns a random number that is between two parameters. You'll use it to make the ball start in a random direction.

Now, open the Playing.swift file under the Game States folder and add an assistant method:

Func randomdirection ()-cgfloat {let
  speedfactor:cgfloat = 3.0
  If Scene.randomfloat (from:0.0, to:100.0) & gt;= {
    Return-speedfactor
  } else {
    return speedfactor
  }
}

The code, like a "Guess coin," returns a positive or negative number. This method becomes random for the initial direction of the ball.

Then in Didenter (from:) Add code to the method:

If Previousstate is Waitingfortap {let ball
  = Scene.childnode (withname:ballcategoryname) as! Skspritenode
  ball.physicsbody!. Applyimpulse (Cgvector (Dx:randomdirection (), Dy:randomdirection ()))
}

When the game enters the Playing state, get the ball sprite, call it's Applyimpulse (_:) method and let it begin to move.

Then in update (deltatime:) Add code to the method:

Let ball = Scene.childnode (withname:ballcategoryname) as! Skspritenode let
maxspeed:cgfloat = 400.0 let

xspeed = sqrt (ball.physicsbody!. VELOCITY.DX * ball.physicsbody!. VELOCITY.DX) let
yspeed = sqrt (ball.physicsbody!. Velocity.dy * ball.physicsbody!. Velocity.dy) let speed

= sqrt (ball.physicsbody!. VELOCITY.DX * ball.physicsbody!. VELOCITY.DX + ball.physicsbody!. Velocity.dy * ball.physicsbody!. Velocity.dy)

if xspeed <= 10.0 {
  ball.physicsbody!. Applyimpulse (Cgvector (Dx:randomdirection (), dy:0.0))
}
if yspeed <= 10.0 {
  ball.physicsbody!. Applyimpulse (Cgvector (dx:0.0, Dy:randomdirection ()))
}

If speed > maxspeed {
  ball.physicsbody!. lineardamping = 0.4
} else {
  ball.physicsbody!. lineardamping = 0.0
}

Update (deltatime:) method is called at the Playing state of each frame. Get the ball object and judge its speed, that is, the speed of movement. If the x or Y speed is lower than a certain threshold, the ball will be stuck in the round bow or straight left-right movement, and if so, we need to exert another force to get it back into a certain angle of motion.

At the same time, the speed increases in the process of moving the ball. If the speed is too fast, you need to increase the linear damping so that the ball slows down.

Now that the Playing state is ready, it's time to start the game.

Back to Gamescene.swift will Touchesbegan (_:with:) Method is replaced by:

Override Func Touchesbegan (_ Touches:set<uitouch>, with Event:uievent?) {
  switch gamestate.currentstate {case is
  Waitingfortap:
    gamestate.enter (playing.self)
    Isfingeronpaddle = True case was

  Playing: let
    touch = Touches.first let
    touchlocation = touch!. Location (in:self)

    if let BODY = Physicsworld.body (at:touchlocation) {
      if body.node!. Name = = Paddlecategoryname {
        Isfingeronpaddle = True
      }
    }

  default: Break
  }
}

Check the current status of the game and make corresponding changes according to the current state. Then, you need to modify the update (_:) The method is:

Override Func Update (_ Currenttime:timeinterval) {
  gamestate.update (deltatime:currenttime)
}

Update (_:) method is called when each frame refreshes. Here you call the update of the Playing State (deltatime:) method to control the speed of the ball.

Build & Run, click on the screen and the state machine is in effect.

Game End Status

The Gameover state occurs when the bamboo brick is destroyed, or when the ball falls to the bottom of the screen.

Open the Gameover.swift file under the Game States folder, in Didenter (from:) method to add:

If Previousstate is Playing {let ball
  = Scene.childnode (withname:ballcategoryname) as! Skspritenode
  ball.physicsbody!. lineardamping = 1.0
  scene.physicsWorld.gravity = Cgvector (dx:0.0, dy: -9.8)
}

When the game enters the Gameover state, the linear damping and gravitational acceleration of the ball are set, and the ball falls to the floor and gradually slows down.

This is the game end state. Next, the code to win or lose is realized. you win and you lose .

The state machine is ready and the game is nearing completion. Now you need to judge the game's winning or losing.

Open Gamescene.swift and add an assistant method:

Func Isgamewon (), Bool {
  var numberofbricks = 0
  self.enumeratechildnodes (withname:blockcategoryname) {
    node, stop in
    numberofbricks = numberofbricks + 1
  }
  return numberofbricks = = 0
}

This method checks all the child nodes in the scene to check that there are still a few bricks left in the scene. For each child node, determine if the name is called Blockcategoryname. If a brick is gone, the player wins and returns true.

Add a property under the Gametstate property declaration:

var Gamewon:bool = False {
  Didset {let
    gameover = Childnode (withname:gamemessagename) as! Skspritenode let
    texturename = Gamewon? "Youwon": "Gameover" let
    texture = sktexture (imagenamed:texturename) let
    actionsequence = skaction.sequence ([Skaction.settexture (texture), 
      skaction.scale (to:1.0, duration:0.25)])

    Gameover.run (actionsequence)
  }
}

Here, you define a Gamewon variable and define its Didset property observer. This allows you to observe the property value changes and make the processing. Here, you will modify the map of the Gamemessage node to Youwon or Gameover and display it to the screen.

NOTE: The property Observer has a parameter that you can use to read the new value (in Willset) and the old value (in Didset) so that when the change occurs, you can compare the two. These 2 parameters are called NewValue and oldValue by default, if you do not provide an alternative name. If you would like to learn more about this, please read the SWIFT programming language: declaration.

Then, modify the Didbegin (_:) Method.

First, in Didbegin (_:) The method starts by adding:

If Gamestate.currentstate is Playing {//
here is the original code ...
}//If statement ends

This prevents the game from collision detection when it is not in the Playing state.

Will this sentence:

Print ("Hit bottom. First contact has been made. ")

To be replaced by:

Gamestate.enter (gameover.self)
Gamewon = False

When the ball touches the bottom of the screen, the game ends.

Replace//TODO: One sentence is replaced by:

If Isgamewon () {
  gamestate.enter (gameover.self)
  Gamewon = True
}

The game wins when all the bricks are crushed.

Finally, in Touchesbegan (_:with:) Before the default branch is added:

Case was Gameover: let
  newscene = Gamescene (filenamed: "Gamescene")
  newscene!. ScaleMode =. Aspectfit let
  reveal = Sktransition.fliphorizontal (withduration:0.5)
  Self.view?. Presentscene (newscene!, Transition:reveal)

Your game is finally finished. Build & Run.

Stop Touch

Now that the game is done, let's get to the next level and add some new features to it. You'll add some sound when the ball crashes and the bricks are crushed. A small piece of music will also be added at the end of the game. Finally, add a special particle emitter to the ball, and when it bounces, give it a trail. Add Sound

In order to save time, several sound files have been added to the project. First, open Gamescene.swift, add the following constants, just below the Gamewon variable:

Let Blipsound = skaction.playsoundfilenamed ("Pongblip", Waitforcompletion:false)
Let Blippaddlesound = SKAction . playsoundfilenamed ("Paddleblip", Waitforcompletion:false)
Let Bamboobreaksound = skaction.playsoundfilenamed (" Bamboobreak ", Waitforcompletion:false)
Let Gamewonsound = skaction.playsoundfilenamed (" Game-won ", Waitforcompletion:false)
Let Gameoversound = skaction.playsoundfilenamed ("Game-over", Waitforcompletion:false) /p>

The above defines a bunch of skaction constants, each loading a different sound file. Because they are defined before these actions are used, they are preloaded into memory to prevent the game from appearing in the first play.

then, in Didmove (to:) method, the contacttestbitmask of the set ball is changed to:

    ball.physicsbody!. Contacttestbitmask = Bottomcategory | Blockcategory | Bordercategory | Paddlecategory

Nothing new, you add bordercategory and paddlecategory to the ball's contacttestbitmask to detect collisions between small balls and screen borders and planks.

Modify Didbegin (_:) method to play the corresponding sound according to Firstbody and secondbody:

' ' Swift
/1
if Firstbody.categorybitmask = = ballcategory & & secondbody.categorybitmask = = bordercategory {
  run (blipsound)
}

//2
if Firstbody.categorybitmask = = Ballcategory && Secondbody.categorybitmask = = paddlecategory {
  run ( Blippaddlesound)
}




<div class= "Se-preview-section-delimiter" ></div>
Play Blipsound when the ball bounces off the screen frame. When touching the plank is playing blippaddlesound.

Of course, when the ball hits the bricks, you need to play the broken sound, in Breakblock (node:) Method at the top of the add:

Run (Bamboobreaksound)

Finally, insert this sentence in the Didset viewer of the Gamewon variable:

Run (Gamewon gamewonsound:gameoversound)

There is one more place to change.

We need to add a particle system to the ball, and when it bounces, it leaves a flame-like wake.

In Didmove (to:) Add code to the method:

1 let
Trailnode = Sknode ()
trailnode.zposition = 1
addChild (trailnode)
//2 let
trail = Skemitternode (filenamed: "Balltrail")!
3
Trail.targetnode = Trailnode
//4
ball.addchild (trail)
Creates a new sknode that is used as the TargetNode property of the particle system. Create a skemitternode from the Balltrail.sks file. Set its targetnode to Trailnode. This will fix the particles so that they leave a trace instead of following the ball motion. Bind the Skemitternode to the ball by AddChild way.

That's it-you're done. Build & Run, your game is enhanced to look like this:

End

You can download the final completed project from here.

This is an example of a simple escape game. Once you've done it, you can add more content. You can add points, or give bricks a health value, add various types of bricks, the ball must hit the bricks several times to destroy them. You can add some bricks that will be rewarded, or bricks that can raise the level.

If you want to learn more about the course of the Sprite Kit, you can read our 2D ios& TvOS game Tutorial.

This book teaches you all about making IOS & TvOS games-including physics engines, tile maps, particle systems, and how to get your game "bonus points" with some landscaping and special effects.

Hope you like this tutorial, if you have any questions and comments, please leave a comment below.

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.