[Swift 2] Day 6 - Game Challenge #1 Solution + Demos


#1

Post your solution code here for the Game Challenge #1

  • See how to post images, code, text, and videos from youtube in video 4

#2

Checkout my game!

for i in 0...100 {

    var direction = CGFloat(drand48() * 2 - 1)  // [0, 2] - 1 = [-1, 1]
    
    var speed = CGFloat(200) // maxSpeed
    
    var xSpeed = direction * speed
    
    println("xSpeed: \(xSpeed)")
}

#3

Hi @PaulSolt
I didn’t add much to the game, I just applied different types of asteroids (the two types)
and here is how I did the random astroid type (it’s a little missy but I did it in day 3 I guess ! so I did it with what I knew :slight_smile: )

    func CreateAnAsteroid() -> SKSpriteNode {
        var type = "Asteroid1"
        var ast = arc4random_uniform(2)
        if ast == 1 {
            type = "Asteroid1"
        }else{
            type = "Asteroid2"
        }
        var asteroid = SKSpriteNode(imageNamed: type)
        asteroid.name = "asteroid"
//        asteroid.physicsBody?.mass = 1
        asteroid.size.width = 100
        asteroid.size.height = 100
        // Random Position !
         asteroid.physicsBody = SKPhysicsBody(texture: asteroid.texture, size: asteroid.size)
//        bit Mask
        
        asteroid.physicsBody?.categoryBitMask = asteroidsCategory
        asteroid.physicsBody?.collisionBitMask = shipCategory
        asteroid.physicsBody?.contactTestBitMask = shipCategory
        
        
        
//        asteroid.physicsBody?.categoryBitMask = asteroidsMask
      var random = CGFloat(arc4random_uniform(UInt32(frame.size.width)))
        asteroid.position = CGPoint(x: random, y: frame.size.height)
       
        
        
        
        return asteroid
    }

and here is my project file -it’s a real miss- I was testing and playing with code :slight_smile:

Thanks :smile:


#4

Nice @keloa !

My code is always in flux when I’m working on a new feature. As you’re learning or figuring things out you’ll have a lot of ideas for what to do.

Usually after things are working I’ll go in and clean up, removing old code/comments that I’m not using any more.


#5

Here are some screenshots (and youtube video) of my game.

So far it has the following features:

  • As the score increases, asteroids become more numerous
  • Custom artwork for everything!
  • Player has a ‘shield’ that decreases each time it is hit. The shield can be recharged if hit by a ‘$’ asteroid
  • Asteroids have a shrink SKAction to make them disappear smoothly
  • Asteroids can be destroyed if the player taps on them
  • Two images are used for the restart button (for the pressed and unpressed states)

Youtube video:


#6

Awesome work @MichaelPeters !

Fun art style.

I’d recommend doing a little trigonometry to get the bombs to orient in the direction they are traveling (alternatively I think an SKConstraint could make this work).

Links

  1. https://developer.apple.com/library/mac/documentation/SpriteKit/Reference/SKConstraint_Ref/
  2. http://stefansdevplayground.blogspot.com/2014/09/howto-implement-targeting-or-follow.html
  3. http://www.raywenderlich.com/57368/trigonometry-game-programming-sprite-kit-version-part-1

#7

Hi Paul. Excellent course! Thanks much.
I implemented some of the challenges differently but found your ways to be cleaner.
Also, when the asteroids bounce they curve and begin to fall down again which is not the case in space. I used the didEndContact function to turn off the gravity so the asteroids would act more like billiard balls. This seems to work but I’m not sure if there are some snags I’m not aware of.
For example, if the asteroid shoots off screen to the right, left or up it will not be removed (but the nodes seem to decrement anyway).

   if asteroid.position.y < 0 - asteroid.calculateAccumulatedFrame().height - 10
    // -10 is slightly off screen before it is removed
  {
    asteroid.removeFromParent() // Removes the asteroid
  }

or

  if asteroid.position.x < 0 - asteroid.calculateAccumulatedFrame().width ||
  asteroid.position.x > self.frame.size.width + asteroid.calculateAccumulatedFrame().width ||
  asteroid.position.y < 0 - asteroid.calculateAccumulatedFrame().height ||
  asteroid.position.y > self.frame.size.height + asteroid.calculateAccumulatedFrame().height + 200
// +200 if above starting points of asteroids
  {
    asteroid.removeFromParent() // Removes the asteroid
  }

seems like a lot of work to catch all directions

  func didEndContact(contact: SKPhysicsContact) {
    // Remove gravity from the asteroid so it continues in bounced direction
    
    if contact.bodyA.contactTestBitMask == asteroidCategory ||
      contact.bodyB.contactTestBitMask == asteroidCategory
    {
      contact.bodyA.affectedByGravity = false
      contact.bodyB.affectedByGravity = false
    }
  }

#8

Yes gravity is something you probably don’t want if you’re going for accuracy. You can set the physics world gravity to zero instead.

Games are all about tweaking. Lots of things are faked or fudged. It’s up to you to decide the rules.


#9

Here is another solution you can do. Here is a piece of my code:

  if contact.bodyA.categoryBitMask == asteroidCategory2 && contact.bodyB.categoryBitMask == shipCategory && slowTrue == false ||
        contact.bodyB.categoryBitMask == asteroidCategory2 && contact.bodyA.categoryBitMask == shipCategory && slowTrue == false {
            
            var asteroidBody: SKPhysicsBody!
            let contactVector = contact.contactNormal
            let contactImpulse = contact.collisionImpulse

// playerShip.physicsBody?.applyImpulse(CGVector(dx: -contactVector.dx, dy: -contactVector.dy))

            // do stuff
            if contact.bodyA.categoryBitMask > contact.bodyB.categoryBitMask {
                
                //body A is the Asteroid
                asteroidBody = contact.bodyA
                asteroidBody.node?.removeFromParent()
                
                
            } else {
                //body B is the Asteroid
                asteroidBody = contact.bodyB
                asteroidBody.node?.removeFromParent()
            }

Once you fhave found which body is the Asteroid what you can do is say AsteroidBody.affectedByGravity = false

So you’ll have the gravity right until the collision… :wink:


#10

Thanks. Great ideas.


#11

Here is a picture of what I’ve done so far.
I’ve added the ISS slowly crawling down, simulating even more the direction of the scene.
Added rockets that can be shot out and recharged when the blue asteroids gets collected.
I also added a health indicator, that will change colors: green when not hit, blue on 1 hit and red on 2 hits taken, then you die :wink:

For the asteroids, rockets and what not, if you really want to organize your project you should separate everything in classes. I am gonna give you an example of how to do that for the rocket I did.
First get a picture of the rocket or whatever projectile you want and add it to your project.
Then create the class for it. Since it’s gonna be a Node with a texture ( the picture ) , we’ll subclass from SKSpriteNode. Create a new Cocoa Touch Class file , subclass from SKSpriteNode:

    //
//  rocket.swift
//  Smasharoids
//
//  Created by Razvigor Andreev on 2/7/15.
//  Copyright (c) 2015 Razvigor Andreev. All rights reserved.
//

import UIKit
import SpriteKit

class rocket: SKSpriteNode {

override init() {
    
    let texture = SKTexture(imageNamed: "rocket")
    super.init(texture: texture, color: nil, size: texture.size())
    
    //define the basics for when our Node gets initialized
    self.physicsBody = SKPhysicsBody(texture: self.texture, size: self.size)
    self.physicsBody?.categoryBitMask = rocketCategory  // asteroid
    self.physicsBody?.collisionBitMask = asteroidCategory | asteroidCategory2 | asteroidCategory3
    self.physicsBody?.contactTestBitMask = asteroidCategory | asteroidCategory2 | asteroidCategory3
    self.zPosition = 9
    self.physicsBody?.dynamic = true
    self.physicsBody?.affectedByGravity = false
    self.xScale = 0.6
    self.yScale = 0.6
    self.name = "rocket"
    
    //generate emmitter ( the rocket flare behind the rocket )
    var rocketFlare: SKEmitterNode!
    rocketFlare = SKEmitterNode(fileNamed: "rocketFlare")
    rocketFlare.position = CGPoint(x: 0, y: 0)
    rocketFlare.name = "rocketFlare"
    rocketFlare.zPosition = 8
    
    //add the flare to the rocket
    self.addChild(rocketFlare)
    
    }

required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}
   }

Then, back in our GameScene.swift, we’ll create our shooting action:

  func shoot() {

   
    let rocketFired = rocket()
    
    rocketFired.position = CGPoint(x: playerShip.position.x, y: playerShip.position.y + 50)
    rockets = false
    shootButton.hidden = true

    addChild(rocketFired)
    rocketFired.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 20))
    }

So, whenever you want to shoot a rocket out, or link it a button as I have, just call shoot()

Good luck :wink: Cheers !

P.S. - for this to work you have to make your categories for bitmasks public, personally I create a new empty Swift file, call it Global.swift and put all my global stuff there, that I am gonna reuse.
There are a few different ways of doing it, but that’s how I do it.

Here is an example:

    //
//  Global.swift
//  Smasharoids
//
//  Created by Razvigor Andreev on 2/7/15.
//  Copyright (c) 2015 Razvigor Andreev. All rights reserved.
//

import Foundation


// Bit Masks
let shipCategory: UInt32 = 0x1 << 0
let asteroidCategory: UInt32 = 0x1 << 1
let asteroidCategory2: UInt32 = 0x1 << 2
let asteroidCategory3: UInt32 = 0x1 << 3
let bottomCategory: UInt32 = 0x1 << 4
let borderCategory: UInt32 = 0x1 << 5
let rocketCategory: UInt32 = 0x1 << 6

#12

wow that looks amazing would it be possible if you could post the code of the whole entire page as i have been falling well behind and need to catch up majorly thank you.


#13

I’ll probably upload all of it somewhere after I clean it up, probably by the end of the week. If you are new to this it’s probably best to build it from scratch, as that’s the only way you really learn. Just go through Paul’s videos and code it little by little.


#14

Found a problem, that I can’t figure out :smile:
The UI elements linking works fine, but if I reload the scene it’s gone.
So let’s say I want to reload the scene upon death like this:

func restartScene() {
        
        self.removeAllChildren()
        
        let scene = GameScene.unarchiveFromFile("GameScene") as GameScene
        scene.scaleMode = .AspectFill
        let delay = SKAction.waitForDuration(0.2)
        let transitionToLevel = SKAction.runBlock({
            
            let transition = SKTransition.doorwayWithDuration(0.2)
            
            self.view?.presentScene(scene, transition: transition)
            
        })
        runAction(SKAction.sequence([delay, transitionToLevel]))
        
        
    

    } 

now all my buttons don’t link to the GameScene…
Ideas ? :slight_smile:


#15

I realize what the problem is…you create the links when the initial View was presented. Once you change the scene the links are no longer there, as they were only valid for the original scene. Somehow we gotta have this method put into something like didLoadNewScene :smiley: Hopefully I’ll have some time tonight to figure this out :smiley:


#16

Ok, I was able to make this with loading a view from Nib, or creating a custom view for the menu instead. Here is the example I made for it.
It’s a bit more complex than just linking the UI elements, but it will work when called on any scene.


#17

hi please can you upload all of your code as i by mistake deleted all of mine trying to fix an error. EVEN if yours has an error please can you post full code some were thank you.


#18

I actually ended up making things worse, as I was working on implementing menu items and in the end the project now completely crashes :slight_smile: I am gonna work on it today and if I finish it up I’ll upload it.
One thing to draw out of this experience is - lay out your project before you start. Draw everything you’ll want on it - main menu, scores, high score, game over scene…etc. Then build a framework out of this. Once you have this working and everything transitions perfectly - then start building the game on top of it. I think I just did the most common mistake, that they were talking about on the Sprite Kit best practices video - got too excited and just got carried away, adding things in a sloppy matter…well, that makes things impossible to troubleshoot later on :smiley:


#19

Hey, mate, I am still in the middle of nowhere, so here is my GameScene file as it is right now, this should get you started. It’s a relatively a large file, so you might get lost , there are not nearly as much pseudocode as I would wish, but that’s how it is at this moment.
Hope that helps :slight_smile:

P.S. - btw you know Paul puts a link of the completed code at the end of each video, so you could just download that, it it probably in much better shape than mine. Again, I think it’s always better to start from scratch, but that’s up to you.


#20

Thank you so much I have done most things I am still to master the rocket shooting but I am nearly finished with the high score. I am only 13 so please could you tell me the code for making the high score as i’m finding it difficult thank you.
benjamin rosenfeld