The previous Xcode SpriteKit Game Primer Tutorial as shown below raved about the virtues of Xcode’s SpriteKit Game project’s cross-platform talents. We leave you to read about that below, and move on, today, to a more realistic “Collision” view of a SpriteKit Game because, as most of us know, a lot of the video games and mobile games we are familiar with, work with the need to “do” the Physics regarding momentum and “Collisions”.
Lots of SpriteKit Games have, you guessed it, Sprite objects, and it is explaining “Sprites” that we first need to talk about these Swift 3 (or you can use Objective-C if you like) coded projects that if left in a default working environment having …
- Scenes … with …
- Actions
… can have their “Sprites” or other “visual elements” included via …
- Scene GameScene.sks Scene Builder visual method of scene building … and/or …
- Scene GameScene.swift Swift 3 coding method to dynamically add and modify the “Sprites” and other “visual elements” along with the associated “Action” logics
Looking at it as a programmer, would not recommend the limitations of just involving Scene GameScene.sks Scene Builder but it is there for you experts of methods similar to the good ol’ IBOutlet linking days. Below we mainly involve Scene GameScene.swift Swift 3 coding methods, but do, with our “obstacle”, link a Scene GameScene.sks Scene Builder “visual element” Sprite with the Scene GameScene.swift Swift 3 code.
It seems to me we start life fascinated by collisions, with many early learning tools ready for baby’s gnawing away at a plaything in their mouth, one of life’s early “collision” places. And think of all those bowls of food carefully pushed off high chairs … “Collisions 101”.
Behind the scenes of you, the Xcode SpriteKit developer starting to “Animate” your “visual element” Sprites and others, will reach a point, in all likelihood, that to mimic real life when these “visual element” bodies meet the “Physics” of Xcode SpriteKit project code behind the scenes will be obeying the “law of momentum” which can be stated … thanks to Momentum Conservation Principle – The Physics Classroom …
The law of momentum conservation can be stated as follows. For a collision occurring between object 1 and object 2 in an isolated system, the total momentum of the two objects before the collision is equal to the total momentum of the two objects after the collision.
Believe me, though, you don’t need to know more than that, and that there is “Gravity” (or “ytivarg” for people reading this on your heads), to write SpriteKit Game code involving some “Physics” SpriteKit Project code functionality help.
We have a long list of links that helped us work this tutorial below, but we want to turn your attention, first to a good read at Collision detection: SKPhysicsContactDelegate as a precursor to our own experience of things we need to do to adapt a project that started out like Xcode SpriteKit Game Primer Tutorial‘s SpriteKit project where we have one “obstruction” to a “rocket” Sprite, with its flare, that the user can zoom around the “obstacle” to clock up the elapsed (last) distance without colliding with the “obstruction”.
- Xcode Swift SpriteKit project GameScene.sks … Show the Object Library at right … drop Colour Sprite into the Scene … Show the Attributes Inspector … gave it a name of “ourobstruction” …
- Found a good “rocket” image thanks to Free Icons PNG which we modified for use via download to disk and then via …
- Xcode New -> File -> iOS -> Resource -> Asset Catalog -> drag over various versions of rocket.png as above into the required slots
- GameScene.swift … class GameScene: SKScene, SKPhysicsContactDelegate {
// rest of Swift code
} - GameScene.swift …
fileprivate var label : SKLabelNode?
fileprivate var spinnyNode : SKShapeNode?
fileprivate var ylight : SKLightNode?
fileprivate var ourcs : SKSpriteNode?
fileprivate var ourobstacle : SKSpriteNode?
fileprivate var ouran : SKAudioNode?
public var anobstacle = SKSpriteNode()
//init your categoryBitMask :
public var carCategory:UInt32 = 0x1 << 0 public var slowCarCategory:UInt32 = 0x1 << 1
class func newGameScene() -> GameScene {
// Load 'GameScene.sks' as an SKScene.
guard let scene = SKScene(fileNamed: "GameScene") as? GameScene else {
print("Failed to load GameScene.sks")
abort()
}
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
return scene
}
func lightWithFinger(at pos: CGPoint){
if let yourcs = self.ourcs?.copy() as! SKSpriteNode? {
yourcs.position = pos
yourcs.name = "notspinny"
yourcs.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 1, height: 1))
yourcs.physicsBody?.categoryBitMask = carCategory
yourcs.physicsBody?.collisionBitMask = slowCarCategory
yourcs.physicsBody?.contactTestBitMask = slowCarCategory
yourcs.physicsBody?.isDynamic = false
self.addChild(yourcs)
}
if let plight = self.ylight?.copy() as! SKLightNode? {
plight.position = pos
plight.lightColor = SKColor.orange
plight.categoryBitMask = 0b0001
plight.isEnabled = true
self.addChild(plight)
}
}
- GameScene.swift …
func setUpScene() {
// Get sprite node
let pl = CGPoint(x: 0.0, y: 0.0)
ourcs = SKSpriteNode(imageNamed: "rocket.png")
ourcs?.name = "notspinny"
ourcs?.position = pl
ourcs?.physicsBody?.categoryBitMask = carCategory
ourcs?.physicsBody?.collisionBitMask = slowCarCategory
ourcs?.physicsBody?.contactTestBitMask = slowCarCategory
ourcs?.physicsBody?.isDynamic = true
let negativeShader = SKShader(source: "void main() { " +
" gl_FragColor = vec4(1.0 - SKDefaultShading().rgb, SKDefaultShading().a); " +
"}")
self.ourcs?.shader = negativeShader
self.ourcs?.anchorPoint = CGPoint(x: 0.5, y: 1.0)
// Get light node
if let ylight = self.childNode(withName: "light") as? SKLightNode {
ylight.position = pl //self.convertPoint(toView: self.view)
ylight.lightColor = SKColor.orange
ylight.categoryBitMask = 0b0001
ylight.isEnabled = true
}
// more setUpScene code below
}
- GameScene.swift …
#if os(watchOS)
override func sceneDidLoad() {
self.setUpScene()
if let anobstacleNode = self.childNode(withName: "ourobstacle") as? SKSpriteNode {
anobstacle = anobstacleNode
if let atextureNode = self.childNode(withName: "rocket") as? SKTexture {
let rocketAnimation = SKAction.animate(with: [atextureNode],
timePerFrame: 0.1)
anobstacle.run(rocketAnimation)
}
anobstacle.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 10, height: 10))
anobstacle.physicsBody?.categoryBitMask = slowCarCategory
anobstacle.physicsBody?.collisionBitMask = carCategory
anobstacle.physicsBody?.contactTestBitMask = carCategory
anobstacle.name = "obstacle"
anobstacle.physicsBody?.isDynamic = false
self.addChild(anobstacle)}
physicsWorld.contactDelegate = self
}
#else
override func didMove(to view: SKView) {
self.setUpScene()
if let anobstacleNode = self.childNode(withName: "ourobstacle") as? SKSpriteNode {
anobstacle = anobstacleNode
if let atextureNode = self.childNode(withName: "rocket") as? SKTexture {
let rocketAnimation = SKAction.animate(with: [atextureNode],
timePerFrame: 0.1)
anobstacle.run(rocketAnimation)
}
anobstacle.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 10, height: 10))
anobstacle.physicsBody?.categoryBitMask = slowCarCategory
anobstacle.physicsBody?.collisionBitMask = carCategory
anobstacle.physicsBody?.contactTestBitMask = carCategory
anobstacle.name = "obstacle"
anobstacle.physicsBody?.isDynamic = false
self.addChild(anobstacle)
}
physicsWorld.contactDelegate = self
}
#endif
- GameScene.swift … the Collision Detection function …
func didBegin(_ contact: SKPhysicsContact)
{
if contact.bodyA.node?.name == "obstacle" {
totalDistance = pow(totalDistance,0.5)
if totalDistance != CGFloat(0.0) {
let userInfo = ["message": totalDistance];
// Get label node from scene and store it for use later
self.label = self.childNode(withName: "//helloLabel") as? SKLabelNode
if let label = self.label {
label.text = totalDistance.description
label.alpha = 1.0
label.run(SKAction.fadeIn(withDuration: 2.0))
}
}
totalDistance = CGFloat(0.0)
} else if contact.bodyB.node?.name == "obstacle" {
totalDistance = pow(totalDistance,0.5)
if totalDistance != CGFloat(0.0) {
// Get label node from scene and store it for use later
self.label = self.childNode(withName: "//helloLabel") as? SKLabelNode
if let label = self.label {
label.text = totalDistance.description
label.alpha = 1.0
label.run(SKAction.fadeIn(withDuration: 2.0))
}
}
totalDistance = CGFloat(0.0)
} else if ((contact.bodyA.node?.position.x)! < CGFloat(100.0) && (contact.bodyA.node?.position.y)! < CGFloat(100.0) && (contact.bodyA.node?.position.x)! > CGFloat(0.0) && (contact.bodyA.node?.position.y)! > CGFloat(0.0)) {
totalDistance = pow(totalDistance,0.5)
if totalDistance != CGFloat(0.0) {
// Get label node from scene and store it for use later
self.label = self.childNode(withName: "//helloLabel") as? SKLabelNode
if let label = self.label {
label.text = totalDistance.description
label.alpha = 1.0
label.run(SKAction.fadeIn(withDuration: 2.0))
}
}
totalDistance = CGFloat(0.0)
} else if (penUp) {
lastX = (contact.bodyA.node?.position.x)!
lastY = (contact.bodyA.node?.position.y)!
penUp = false;
} else {
totalDistance += (((contact.bodyA.node?.position.x)! - lastX) * ((contact.bodyA.node?.position.x)! - lastX))
totalDistance += (((contact.bodyA.node?.position.y)! - lastY) * ((contact.bodyA.node?.position.y)! - lastY))
lastX = (contact.bodyA.node?.position.x)!
lastY = (contact.bodyA.node?.position.y)!
print(totalDistance)
}
}
fileprivate var label : SKLabelNode?
fileprivate var spinnyNode : SKShapeNode?
fileprivate var ylight : SKLightNode?
fileprivate var ourcs : SKSpriteNode?
fileprivate var ourobstacle : SKSpriteNode?
fileprivate var ouran : SKAudioNode?
public var anobstacle = SKSpriteNode()
//init your categoryBitMask :
public var carCategory:UInt32 = 0x1 << 0
public var slowCarCategory:UInt32 = 0x1 << 1
var totalDistance = CGFloat(0.0);
var penUp = true;
var lastX = CGFloat(0.0);
var lastY = CGFloat(0.0);
class func newGameScene() -> GameScene {
// Load 'GameScene.sks' as an SKScene.
guard let scene = SKScene(fileNamed: "GameScene") as? GameScene else {
print("Failed to load GameScene.sks")
abort()
}
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
return scene
}
func lightWithFinger(at pos: CGPoint){
if let yourcs = self.ourcs?.copy() as! SKSpriteNode? {
yourcs.position = pos
yourcs.name = "notspinny"
yourcs.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 1, height: 1))
yourcs.physicsBody?.categoryBitMask = carCategory
yourcs.physicsBody?.collisionBitMask = slowCarCategory
yourcs.physicsBody?.contactTestBitMask = slowCarCategory
yourcs.physicsBody?.isDynamic = false
self.addChild(yourcs)
}
if let plight = self.ylight?.copy() as! SKLightNode? {
plight.position = pos
plight.lightColor = SKColor.orange
plight.categoryBitMask = 0b0001
plight.isEnabled = true
self.addChild(plight)
}
}
And you can see GameScene.swift Swift 3 code here.
And of great help, thanks, were …
- An iOS 10 Sprite Kit Collision Handling Tutorial
- ios – Displaying a UIAlertController in GameScene (SpriteKit/Swift) – Stack Overflow
- Collision detection: SKPhysicsContactDelegate – a free Hacking with Swift tutorial
- collision detection – didBegin contact not being called in Swift – Stack Overflow
- ios – How to set contactDelegate for physicsWorld in SpriteKit? – Stack Overflow
- How to add physics to an SKSpriteNode – free Swift 3 example code
- collision in swift 3 sprite kit Xcode 8 – Stack Overflow
- ios – Detecting collisions in sprite kit – Stack Overflow
- ios – Detecting collisions in sprite kit – Stack Overflow
- SKAction – SpriteKit | Apple Developer Documentation
Previous relevant Xcode SpriteKit Game Primer Tutorial is shown below.
We’re always on the lookout for the word “cross” within I.T. circles (not because we have any anger management issues, but), because it might mean some relief from “platform fatigue”. By that, we mean that the number of different devices and platforms you write code for, and hope to achieve just the one codebase, makes you pay attention, especially when working in an IDE when the word “cross” is prominent.
And so it was as we upgraded our MacBook Pro’s Xcode to version 8.3.1, and that set into play a revisit of this great IDE and a revisit of one of the great anticipatory parts of a good upgrade, the revisit to an IDE’s File -> New Project functionality. Xcode did not disappoint, offering templates for Apple platforms …
- iOS … Apple iPhones and iPads and iPods
- watchOS … Apple watch
- tvOS … Apple TV
- macOS … Apple Mac OS X … as with this MacBook Pro … and … da, da da da da da, da da da …
- Cross-platform … yay!!!! … feature today’s project of choice …
Cross-platform SpriteKit Game
Now the test using “Cross-platform SpriteKit Game” would be, for us, is integrating with all those other (albeit, only, Apple) platforms be seamless and easy? And turn away now in both a horizontal and vertical direction to avoid spoiler alert … you were warned … yes, as long as you have an Apple Developer Signup going if you might send your game to the Apple Store, otherwise you can do unit testing with device simulators. And that is where, for us, those Apple white leads become so important for real device testing with the MacBook Pro’s USB ports (hang on to your older version MacBook Pros?!) interfacing hardwarewise with the Apple white leads and softwarewise with the MacBook Pro’s iTunes desktop application, first up, where any Syncing and/or Ejecting of those real devices can take place, and be the springboard for Xcode projects to Product -> Destination (or via some much more self-explanatory “top of screen” ways, now, with Xcode 8.3.1) to manage lots of device scenarios. And you’ll want that using Xcode’s File -> New Project -> Cross-platform -> Cross-platform SpriteKit Game work, because you’ll get a lot of work done fast. By the way, we pick Objective-C as the language of project rather than the newer Swift for today.
Which brings us to today’s “Cross-platform SpriteKit Game” work. Yes, you guessed it … Hello World … but we do a few easy embellishments, which we’ll outline below, but we also say that you may get from our PDF slideshow presentation today …
- Open Xcode (IDE) desktop application (now 8.3.1 for us) on MacBook Pro
- File -> New Project -> Cross-platform -> Cross-platform SpriteKit Game … see slide 3 … etcetera etcetera etcetera
- ensure Project -> General (tab) -> Signing -> Team involves a real option, if you intend to do more than unit testing and debugging in Simulators
- ensure Project -> General (tab) -> Deployment Info -> Deployment Target involves a version low enough to cover the operating system of all your devices … we lowered it to 10.0 to cater for our iPad … see slide 7
- we add some Emojis into the Share folder’s GameScene.sks “scene”‘s “helloLabel”‘s Attribute Inspector’s “Text” field via Edit -> Emoji and Symbols submenu … see slide 6
- we change the Share folder’s GameScene.sks “scene”‘s “helloLabel”‘s Attribute Inspector’s we change “Rotation”, “Color”, “font” fields (which is pretty intuitive) for some variety … see slide 10
- we start to look at the Share folder’s Actions.sks “scene”‘s “Pulse”‘s Attribute Inspector’s “Fade In”, “Fade Out”, “Scale” actions … see slide 11 and slide 12 (to see its context in total time)
- we want to add code to introduce audio to application and start looking at GameScene.h and GameScene.m where code changes will take place … see slide 15 and slide 16 and slide 17 and slide 18 … so, what we do for …
- adding audio “notes of a piano” (that we had floating around) into the project… and, before we forget, thanks to this very usefiul link here …
under iOS -> Support Files (group) TwoFinger->Gesture New Group … call it ‘resource’ …
agree to any Bundle Group relationships … that makes this Group permeate to all the different Target platform apps when building …
drag those audio files to the ‘resource’ Group …
check in Project -> Build Phases -> Copy Bundle Resources that those audio files happen that (anticipated) way … otherwise … Gooooooooogggggggggllllleee! - low sound at a touch down or mouse down event …
GameScene.h changes as per …
//
// GameScene.h
// Cross Two
//
// Created by pgAgent on 12/4/17.
// Copyright © 2017 RJM Programming. All rights reserved.
//
#import <SpriteKit/SpriteKit.h>
#import <AVFoundation/AVFoundation.h>
@interface GameScene : SKScene
+ (GameScene *)newGameScene;
@end
GameScene.m changes up the top as per …
//
// GameScene.m
// Cross Two
//
// Created by pgAgent on 12/4/17.
// Copyright © 2017 RJM Programming. All rights reserved.
//
#import "GameScene.h"
@implementation GameScene {
SKShapeNode *_spinnyNode;
SKLabelNode *_label;
AVAudioPlayer *player;
}
// ... more code below
GameScene.m changes up the top of (void)setUpScene as per us making that sound be the lowest A on your normal piano (ie. A0.mp3) …
// ... more code above
- (void)setUpScene {
// Set up play sound
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/A0.mp3", [[NSBundle mainBundle] resourcePath]]];
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
player.numberOfLoops = 0;
// Get label node from scene and store it for use later
_label = (SKLabelNode *)[self childNodeWithName:@"//helloLabel"];
_label.alpha = 0.0;
[_label runAction:[SKAction fadeInWithDuration:2.0]];
// ... more code below
GameScene.m changes bottom of (void)touchesBegan as per …
// ... more code above
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[_label runAction:[SKAction actionNamed:@"Pulse"] withKey:@"fadeInOut"];
for (UITouch *t in touches) {
[self makeSpinnyAtPoint:[t locationInNode:self] color:[SKColor greenColor]];
}
[player play];
}
// ... more code below
GameScene.m changes bottom of (void)mouseDown as per …
// ... more code above
- (void)mouseDown:(NSEvent *)event {
[_label runAction:[SKAction actionNamed:@"Pulse"] withKey:@"fadeInOut"];
[player play];
[self makeSpinnyAtPoint:[event locationInNode:self] color:[SKColor greenColor]];
}
// ... more code below
- some higher sound Actions.sks actions … which requires only GUI style work …
at the Share folder’s Actions.sks “scene”‘s “Pulse”‘s Attribute Inspector’s Object Library’s “PlaySoundFileNamed Action” drag into the desired timeslot and fill out “Start Time”, “Duration” and “Filename” (from a dropdown that should contain entries for each of those *.mp3 files you added into the project earlier) …
repeat at different times for some “bird chirping” feeling randomosity, as required
- adding audio “notes of a piano” (that we had floating around) into the project… and, before we forget, thanks to this very usefiul link here …
- to prepare the ground for the next run USB plug in any real device you want to test run, via USB port and Apple lead …
- to further prepare the ground for the next run, visit Product -> Destination and pick appropriate Simulator or real device …
- to test a device whether simulated or real Product -> Run … and on a real device it will leave an installed version should it build correctly, and that you can revisit and run separate to being hooked up to the MacBook Pro, after you use the MacBook Pro’s iTunes device button Eject option, when you’ve finished testing, and want to unplug the real device … and “let it be mobile”,
manpersonage
Get a few real devices and simulators going … and … “Weird Forest Sounds” (was the vision, but)“an instructional Xcode 8.3.1 Cross-platform SpriteKit Game project tutorial” YouTube video follows (above the MacBook Pro’s fan problems today … cut, cut!).
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.