How to run action on tapping object as opposed to releasing tap? - swift2

Here is a simplified scenekit default scene with the ship. Tap the ship, release, and the ship spins. How do you modify program so that when you tap the ship, the action starts? No worry about releasing or holding tap.
class GameViewController: UIViewController {
override func viewDidLoad() { super.viewDidLoad()
let scene = SCNScene(named: "art.scnassets/ship.scn")!
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light!.type = SCNLightTypeOmni
lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
scene.rootNode.addChildNode(lightNode)
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light!.type = SCNLightTypeAmbient
ambientLightNode.light!.color = UIColor.darkGrayColor()
scene.rootNode.addChildNode(ambientLightNode)
let scnView = self.view as! SCNView
scnView.scene = scene
scnView.allowsCameraControl = true
scnView.showsStatistics = true
scnView.backgroundColor = UIColor.blackColor()
let tapGesture = UITapGestureRecognizer(target: self, action: "handleTap:")
scnView.addGestureRecognizer(tapGesture)
}
func handleTap(gestureRecognize: UIGestureRecognizer) {
let scnView = self.view as! SCNView
// the ship
let ship = scnView.scene!.rootNode.childNodeWithName("ship", recursively: true)!
// the action
let rotateY = SCNAction.repeatActionForever(SCNAction.rotateByX(0, y: 2, z: 0, duration: 1))
let point = gestureRecognize.locationInView(scnView)
let hitResults = scnView.hitTest(point, options: nil)
if hitResults.count > 0 {
let result: AnyObject! = hitResults[0]
// the call
if result.node!.name!.hasPrefix("ship") {
ship.runAction(rotateY)
}
}
}
override func shouldAutorotate() -> Bool { return true }
override func prefersStatusBarHidden() -> Bool { return true }
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone { return .AllButUpsideDown }
else { return .All }
}
override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() }
}

A gesture recognizer recognizes an entire gesture. For a tap that happens when the touch begin and touch end events happen in close proximity. The built in recognizer will only fire once the "tap gesture" is complete and both events are complete.
If you want different behavior on the Touch Begin or Touch End, you will have to handle the low-level events yourself. To do this you can either create your own, custom UIGestureRecognizeror you can create a custom UIView and use the methods in UIResponder like
touchesBegan(_:withEvent:)
Please see the Event Handling Guide for iOS

Related

SceneKIt - how to assign ARSCNView video feed as a texture to a SCNGeometry

Goal: get the video feed from ARSCNView. and assign as a video material to a SceneKIt SCNGeometry.
What I did:
// retrieve the ship node
let ship = scene.rootNode.childNode(withName: "shipMesh", recursively: true)!
// apply AR feed to the ship node material
let material = ship.geometry?.firstMaterial
material!.diffuse.contents = arView.scene.background.contents
Problem: the ship is white, without AR video feed
Full code bellow:
import UIKit
import QuartzCore
import SceneKit
import ARKit
class GameViewController: UIViewController, ARSCNViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let arView = ARSCNView()
// create a new scene
let scene = SCNScene(named: "art.scnassets/ship.scn")!
// create and add a camera to the scene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)
// place the camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
// create and add a light to the scene
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light!.type = .omni
lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
scene.rootNode.addChildNode(lightNode)
// create and add an ambient light to the scene
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light!.type = .ambient
ambientLightNode.light!.color = UIColor.darkGray
scene.rootNode.addChildNode(ambientLightNode)
// retrieve the ship node
let ship = scene.rootNode.childNode(withName: "shipMesh", recursively: true)!
let material = ship.geometry?.firstMaterial
material!.diffuse.contents = arView.scene.background.contents
material!.lightingModel = .constant
// animate the 3d object
//ship.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 1)))
// retrieve the SCNView
let scnView = self.view as! SCNView
// set the scene to the view
scnView.scene = scene
// allows the user to manipulate the camera
scnView.allowsCameraControl = true
// show statistics such as fps and timing information
scnView.showsStatistics = true
// configure the view
scnView.backgroundColor = UIColor.black
// add a tap gesture recognizer
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
scnView.addGestureRecognizer(tapGesture)
}
#objc
func handleTap(_ gestureRecognize: UIGestureRecognizer) {
// retrieve the SCNView
let scnView = self.view as! SCNView
// check what nodes are tapped
let p = gestureRecognize.location(in: scnView)
let hitResults = scnView.hitTest(p, options: [:])
// check that we clicked on at least one object
if hitResults.count > 0 {
// retrieved the first clicked object
let result = hitResults[0]
// get its material
let material = result.node.geometry!.firstMaterial!
// highlight it
SCNTransaction.begin()
SCNTransaction.animationDuration = 0.5
// on completion - unhighlight
SCNTransaction.completionBlock = {
SCNTransaction.begin()
SCNTransaction.animationDuration = 0.5
material.emission.contents = UIColor.black
SCNTransaction.commit()
}
material.emission.contents = UIColor.red
SCNTransaction.commit()
}
}
override var shouldAutorotate: Bool {
return true
}
override var prefersStatusBarHidden: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
}
Create a fresh (out of the box) ARKit SceneKit based App
(the one with the SpaceShip)
Then you modify your viewWillAppear section like this:
Add the DispatchQueue stuff below.
Important: Add a short delay (here 5.0 seconds) to give the AR Subsystem enough time to initialise the Camera.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
// Run the view's session
sceneView.session.run(configuration)
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
let shipMesh = self.sceneView.scene.rootNode.childNode(withName: "shipMesh", recursively: true)
shipMesh?.geometry?.firstMaterial?.diffuse.contents = self.sceneView.scene.background.contents
}
}
I hope, this is what you were looking for.

How draw a rectangle hole on UIBlurEffect and move it on x axis (UIKit)

I'm trying to create blur effect on a view and than add a shape which will show image on this blurred layer (custom video editing functionality)
Currently I'm able to do it only dragging mask view from the right edge:
but when I try to do it from the left edge, I get such a effect:
func configureBlurView() {
let viewHeight: CGFloat = 60
let padding: CGFloat = 10
blurView = UIView()
blurView.layer.cornerRadius = 10
blurView.clipsToBounds = true
blurView.isHidden = true
blurView.translatesAutoresizingMaskIntoConstraints = false
addSubview(blurView)
addConstraints([
blurView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: padding),
blurView.bottomAnchor.constraint(equalTo: stackView.topAnchor, constant: -padding),
blurView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -padding),
blurView.heightAnchor.constraint(equalToConstant: viewHeight)
])
addBlurEffect(for: blurView)
}
private func addBlurEffect(for view: UIView) {
let blurEffect = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
blurEffect.alpha = 0.5
blurEffect.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(blurEffect)
addConstraints([
blurEffect.topAnchor.constraint(equalTo: view.topAnchor),
blurEffect.leadingAnchor.constraint(equalTo: view.leadingAnchor),
blurEffect.bottomAnchor.constraint(equalTo: view.bottomAnchor),
blurEffect.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
private func makeClearHole(rect: CGRect) {
let maskLayer = CAShapeLayer()
maskLayer.fillColor = UIColor.black.cgColor
let pathToOverlay = CGMutablePath()
pathToOverlay.addRect(blurView.bounds)
pathToOverlay.addRect(rect)
maskLayer.path = pathToOverlay
maskLayer.fillRule = .evenOdd
maskLayer.cornerRadius = 10
blurView.layer.mask = maskLayer
}
I'm using touchesMoved method to change orange view dimensions:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard trimmerView.isHidden == false else { return }
if let touch = touches.first{
let currentTouchPoint = touch.location(in: self)
let previousTouchPoint = touch.previousLocation(in: self)
let deltaX = currentTouchPoint.x - previousTouchPoint.x
if trimmerView.bounds.width >= 70 {
if touchStartEdge.middle {
if trimmerViewLeadingConstraint.constant < 10 {
trimmerViewLeadingConstraint.constant = 10
} else if trimmerViewTrailingConstraint.constant > -10 {
trimmerViewTrailingConstraint.constant = -10
} else {
trimmerViewLeadingConstraint.constant += deltaX
trimmerViewTrailingConstraint.constant += deltaX
}
}
if touchStartEdge.leftEdge {
if trimmerViewLeadingConstraint.constant >= 10.0 {
trimmerViewLeadingConstraint.constant += deltaX
} else if trimmerViewLeadingConstraint.constant < 10.0 {
trimmerViewLeadingConstraint.constant = 10
}
}
if touchStartEdge.rightEdge {
if trimmerViewTrailingConstraint.constant <= -10 {
trimmerViewTrailingConstraint.constant += deltaX
} else if trimmerViewTrailingConstraint.constant > -10 {
trimmerViewTrailingConstraint.constant = -10.0
}
}
}
updateProgressBarConstraints()
makeClearHole(rect: CGRect(x: 0, y: 0, width: trimmerView.frame.width, height: trimmerView.frame.height))
UIView.animate(withDuration: 0.10, delay: 0, options: .curveEaseIn) { [weak self] in
self?.layoutIfNeeded()
}
}
}
What I'd like to achieve is to remove blur effect only in bounds of orange view.
Any ideas ?? :)
Thanks for help!!
Couple ways to do this - here's one...
Add a mask to the blur effect view. As the user drags the "trimmer" update the mask.
Here's a quick example...
We'll:
create a stack view with 10 images
overlay that with a masked blur effective view
add a "draggable trimmer view"
when we drag the trimmer, we update the mask
Example View Controller
class TrimmerVC: UIViewController {
var blurView: MaskedBlurView!
let trimmerView = DragView()
let stackView = UIStackView()
override func viewDidLoad() {
super.viewDidLoad()
// respect safe area when we setup constraints
let g = view.safeAreaLayoutGuide
stackView.distribution = .fillEqually
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
stackView.heightAnchor.constraint(equalToConstant: 80.0),
])
// let's add 10 imageviews to the stack view
for i in 1...10 {
if let img = UIImage(systemName: "\(i).circle.fill") {
let imgView = UIImageView(image: img)
imgView.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
stackView.addArrangedSubview(imgView)
}
}
let blurEffect = UIBlurEffect(style: .dark)
blurView = MaskedBlurView(effect: blurEffect)
blurView.alpha = 0.5
blurView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(blurView)
NSLayoutConstraint.activate([
blurView.topAnchor.constraint(equalTo: stackView.topAnchor, constant: 0.0),
blurView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 0.0),
blurView.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: 0.0),
blurView.bottomAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 0.0),
])
trimmerView.backgroundColor = .systemOrange
view.addSubview(trimmerView)
trimmerView.didDrag = { [weak self] newX in
guard let self = self else { return }
self.blurView.clearX = newX - self.stackView.frame.origin.x
}
}
// we'll use this to update the framing when the stack view width changes
// such as on device rotation
var curStackW: CGFloat = -1
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if curStackW != stackView.frame.width {
curStackW = stackView.frame.width
var r = stackView.frame
r.origin.y += r.size.height + 20.0
r.size.width = 160
r.size.height = 40
trimmerView.frame = r
blurView.clearWidth = trimmerView.frame.width
blurView.clearX = 0
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// toggle the trimmer view between
// below the stack view and
// overlaid on the stack view
if trimmerView.frame.origin.y > stackView.frame.origin.y {
let r = stackView.frame
trimmerView.frame.origin.y = r.origin.y - 6.0
trimmerView.frame.size.height = r.height + 12.0
} else {
let r = stackView.frame
trimmerView.frame.origin.y = r.origin.y + r.height + 12.0
trimmerView.frame.size.height = 60.0
}
}
}
Example Draggable "Trimmer" View
class DragView: UIView {
var didDrag: ((CGFloat) -> ())?
let maskLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
maskLayer.fillColor = UIColor.red.cgColor
layer.mask = maskLayer
}
override func layoutSubviews() {
super.layoutSubviews()
let pathToOverlay = CGMutablePath()
pathToOverlay.addRect(bounds)
pathToOverlay.addRect(bounds.insetBy(dx: 20.0, dy: 8.0))
maskLayer.path = pathToOverlay
maskLayer.fillRule = .evenOdd
maskLayer.cornerRadius = 10
}
var touchStartX: CGFloat = 0
var frameStartX: CGFloat = 0
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
touchStartX = touch.location(in: self.superview!).x
frameStartX = self.frame.origin.x
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let loc = touch.location(in: self.superview!)
self.frame.origin.x = frameStartX + (loc.x - touchStartX)
didDrag?(self.frame.origin.x)
}
}
Example Masked Blur View
class MaskedBlurView: UIVisualEffectView {
public var clearWidth: CGFloat = 100 {
didSet { updateMask() }
}
public var clearX: CGFloat = 0 {
didSet { updateMask() }
}
private let maskLayer = CAShapeLayer()
override init(effect: UIVisualEffect?) {
super.init(effect: effect)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
maskLayer.fillColor = UIColor.red.cgColor
layer.mask = maskLayer
}
func updateMask() {
let leftR = CGRect(x: 0, y: 0, width: clearX, height: bounds.height)
let rightR = CGRect(x: clearX + clearWidth, y: 0, width: bounds.width, height: bounds.height)
let bez = UIBezierPath(rect: leftR)
bez.append(UIBezierPath(rect: rightR))
maskLayer.path = bez.cgPath
}
override func layoutSubviews() {
super.layoutSubviews()
maskLayer.frame = bounds
}
}
When running (in landscape orientation) it will start like this:
I placed the "trimmer" view below the stack view to make it a little more clear what's happening.
As we drag the trimmer view, the blur view's mask will be updated:
Tapping anywhere in an empty part of the screen will toggle the trimmer view between "under the stack view" and "overlaid on the stack view":
This was just put together quickly -- you should have no trouble restructuring the code to wrap everything into a single custom view (or however it would work best for your needs).

Looping Videos with XCode ARKit Image Tracking

Hello I'm new into developing and right now I'm working on an AR application which works with image tracking and it should play different videos on different tracked images.
Right now the video stops, how can I loop the video?
Also if the image is not in the camera view, the video continues. Is there a pause function too?
I would be thankful for every hints.
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
#IBOutlet var sceneView: ARSCNView!
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let configuration = ARImageTrackingConfiguration()
if let trackedImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: Bundle.main) {
configuration.trackingImages = trackedImages
configuration.maximumNumberOfTrackedImages = 2
}
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
sceneView.session.pause()
}
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
let node = SCNNode()
if let imageAnchor = anchor as? ARImageAnchor {
let size = imageAnchor.referenceImage.physicalSize
var videoNode = SKVideoNode()
switch imageAnchor.name {
case "Image1":
videoNode = SKVideoNode(fileNamed: "Image1.mp4")
case "Image2":
videoNode = SKVideoNode(fileNamed: "Image2.mp4")
case "Image3":
videoNode = SKVideoNode(fileNamed: "Image3.mp4")
default:
break
}
videoNode.play()
let videoScene = SKScene(size: CGSize(width: 1280, height: 960))
videoScene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
videoScene.addChild(videoNode)
let plane = SCNPlane(width: size.width, height: size.height)
plane.firstMaterial?.diffuse.contents = videoScene
let planeNode = SCNNode(geometry: plane)
plane.firstMaterial?.isDoubleSided = true
planeNode.eulerAngles.x = .pi / 2
node.addChildNode(planeNode)
return node
}
return nil
}
}

How to Added objects in SceneKit SWIFT

I have 2 files that I imported from Blender(3D design program) they are both .dae specifically they are "CampusField1.dae" CampusField is the ground/floor of the game and "Bob.dae" is the Man/character. My question is when I set CampusField1 as the scene how do I get "Bob" in the scene too. And the other question is lets say I export the .dae from blender now I put the file in the game... every things good but then is the animation for Bob already attached to the Bob.dae file or do I have to export something else from blender so that I can run the animation because I don't know what the animation ID would be or how to actually make it run and to actually make Bob do something.
Code:
import UIKit
import QuartzCore
import SceneKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scene = SCNScene(named: "art.scnassets/CampusField1.dae")!
let src = SCNSceneSource(URL: yourSceneURL, options: nil)
let node = src.entryWithIdentifier("Bob", withClass: SCNNode.self) as SCNNode
let animation = node.entryWithIdentifier("yourAnimationID", withClass: CAAnimation.self) as CAAnimation
Full GameController Below!:
import UIKit
import QuartzCore
import SceneKit
//============================================================
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//-------------------------
let scene = SCNScene(named: "art.scnassets/CampusField1.dae")!
let src = SCNSceneSource(URL: yourSceneURL, options: nil)
let node = src.entryWithIdentifier("Bob", withClass: SCNNode.self) as SCNNode
let animation = node.entryWithIdentifier("yourAnimationID", withClass: CAAnimation.self) as CAAnimation
//--------------------------
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
//-----------------------------------------------
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light!.type = SCNLightTypeOmni
lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
scene.rootNode.addChildNode(lightNode)
//-----------------------------------------------
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light!.type = SCNLightTypeAmbient
ambientLightNode.light!.color = UIColor.darkGrayColor()
scene.rootNode.addChildNode(ambientLightNode)
//----------------------------------------------
//_ = scene.rootNode.childNodeWithName("Bob", recursively: true)!
// _ = scene.rootNode.childNodeWithName("CampusField1", recursively: true)!
//--------------------------------------------------------
// Bob.runAction(SCNAction.repeatActionForever(SCNAction.rotateByX(0, y: 2, z: 0, duration: 1)))
let scnView = self.view as! SCNView
scnView.scene = scene
scnView.allowsCameraControl = true
scnView.showsStatistics = false
scnView.backgroundColor = UIColor.whiteColor()
let tapGesture = UITapGestureRecognizer(target: self, action: "handleTap:")
scnView.addGestureRecognizer(tapGesture)
}
func handleTap(gestureRecognize: UIGestureRecognizer) {
let scnView = self.view as! SCNView
let p = gestureRecognize.locationInView(scnView)
let hitResults = scnView.hitTest(p, options: nil)
if hitResults.count > 0 {
let result: AnyObject! = hitResults[0]
let material = result.node!.geometry!.firstMaterial!
SCNTransaction.begin()
SCNTransaction.setAnimationDuration(0.5)
SCNTransaction.setCompletionBlock {
SCNTransaction.begin()
SCNTransaction.setAnimationDuration(0.5)
material.emission.contents = UIColor.blackColor()
SCNTransaction.commit()
}
material.emission.contents = UIColor.yellowColor()
SCNTransaction.commit()
}
}
//==================================================
override func shouldAutorotate() -> Bool {
return true
}
//============================
override func prefersStatusBarHidden() -> Bool {
return true
}
//==========================
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .AllButUpsideDown
} else {
return .All
}
}
//=============================
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
}
The first thing to tell you is you have got two SCNScene; CampusField and Bob. Therefore you need to take out the character node from the Bob scene.
You need to name the node as like from the above figure. And extract that node from the scene as:
let bobScene = SCNScene(named: "Bob.dae")
let bobNode = personScene?.rootNode.childNodeWithName("person", recursively: true)
let campusFieldScene = SCNScene(named: "CampusField1.dae")
campusFieldScene.rootNode.addChildNode(bobNode)

Node not moving after changing scenes

I made a (basic) game, works perfect, goes to GameOverScene, and when it comes back from GameOverScene to GameScene, the player(spritenode) is not moving anymore..
I commented in the GameScene code which functions its about
I get no error from Xcode!
the bug is in the gamescene.swift file(functions: swipedRight + swipedLeft + swipedUp + swipedDown):
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var kikker:SKSpriteNode = SKSpriteNode()
var auto1:SKSpriteNode = SKSpriteNode()
var lastYieldTimeInterval:NSTimeInterval = NSTimeInterval()
var lastUpdateTimerInterval:NSTimeInterval = NSTimeInterval()
let playerCategory:UInt32 = 0x1 << 1
let auto1Category:UInt32 = 0x1 << 0
required init(coder aDecoder:NSCoder) {
fatalError("NSCoder not supported")
}
override init(size:CGSize) {
super.init(size:size)
anchorPoint = CGPoint(x:0, y:1.0)
let background = SKSpriteNode(imageNamed: "bg5")
var auto1:SKSpriteNode = SKSpriteNode(imageNamed: "auto1")
background.position = CGPoint(x:0, y:0)
background.anchorPoint=CGPoint(x:0,y:1.0)
addChild(background)
kikker = SKSpriteNode(imageNamed:"kikker5")
kikker.anchorPoint = CGPoint(x:0.5, y:0.5)
kikker.xScale = 0.22
kikker.yScale = 0.22
self.physicsWorld.gravity = CGVectorMake(0,0)
self.physicsWorld.contactDelegate = self
kikker.physicsBody?.categoryBitMask = playerCategory
kikker.physicsBody?.contactTestBitMask = auto1Category
kikker.physicsBody?.collisionBitMask = 0
kikker.physicsBody?.usesPreciseCollisionDetection = true
kikker.physicsBody = SKPhysicsBody(circleOfRadius: kikker.size.width/2)
kikker.physicsBody?.dynamic = false
kikker.position = CGPointMake(self.frame.size.width/2, -610)
addChild(kikker)
println(kikker.position)
}
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 & auto1Category) != 0 && (secondBody.categoryBitMask & playerCategory) != 0)
{
println("aasda")
aangereden(contact.bodyB.node as SKSpriteNode, player: contact.bodyA.node as SKSpriteNode)
}
}
func addCar(){
var auto1:SKSpriteNode = SKSpriteNode(imageNamed: "auto2")
auto1.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(auto1.size.width/2, auto1.size.height/2) )
auto1.physicsBody?.dynamic = true
auto1.physicsBody?.categoryBitMask = auto1Category
auto1.physicsBody?.contactTestBitMask = playerCategory
auto1.physicsBody?.collisionBitMask = 0
auto1.physicsBody?.usesPreciseCollisionDetection = true
let position : CGFloat = 51 + (CGFloat(arc4random_uniform(4)+2)*(-111))
auto1.anchorPoint = CGPoint(x:0.5, y:0.5)
auto1.position = CGPointMake(-auto1.size.width/2, position)
self.addChild(auto1)
let minDuration = 2.5
let maxDuration = 4.0
let rangeDuration = maxDuration - minDuration
let duration = Int(arc4random()) % Int(rangeDuration) + Int(minDuration)
var actionArray:NSMutableArray = NSMutableArray()
actionArray.addObject(SKAction.moveTo(CGPointMake(375 + auto1.size.width/2, position), duration: NSTimeInterval(duration)))
actionArray.addObject(SKAction.removeFromParent())
auto1.runAction(SKAction.sequence(actionArray))
}
println("DOOD")
player.removeFromParent()
}
func updateWithTimeSinceLastUpdate(timeSinceLastUpdate:CFTimeInterval){
lastYieldTimeInterval += timeSinceLastUpdate
if (lastYieldTimeInterval > 2.5){
lastYieldTimeInterval = 0
addCar()
}
}
//#1 function not working after changing scenes: the function is executing, the println works
but for some reason the runAction doesnt do its job, the node(kikker) is not moving as it should
func swipedRight1(sender:UISwipeGestureRecognizer){
var naarRechts = SKAction()
var positionX = kikker.position.x
println("right2")
if(kikker.position.x<200){
println(positionX)
positionX = kikker.position.x + 125
println(positionX)
naarRechts = SKAction.moveToX(positionX , duration: 0.25)
kikker.zRotation=(-1.570)
kikker.runAction(naarRechts)
}
}
//#2 function not working, same story
func swipedLeft1(sender:UISwipeGestureRecognizer){
var naarLinks = SKAction()
var positionX = kikker.position.x
if(kikker.position.x>150){
positionX = kikker.position.x - 125
naarLinks = SKAction.moveToX(positionX , duration: 0.25)
kikker.zRotation=(1.570)
kikker.runAction(naarLinks)
}
}
//#3 function not working, same story
func swipedDown1(sender:UISwipeGestureRecognizer){
var naarBeneden = SKAction()
var positionY = kikker.position.y
if(kikker.position.y>(-600)){
positionY = kikker.position.y - 111
naarBeneden = SKAction.moveToY(positionY , duration: 0.25)
kikker.zRotation=3.141
kikker.runAction(naarBeneden)
}
}
//#4 function not working, same story
func swipedUp1(sender:UISwipeGestureRecognizer){
var naarBoven = SKAction()
var positionY = kikker.position.y
if(kikker.position.y < (-60)){
positionY = kikker.position.y + 111
naarBoven = SKAction.moveToY(positionY, duration: 0.25)
kikker.zRotation=0
kikker.runAction(naarBoven)
}
if(positionY > (-60)){
var gameOverScene:SKScene = GameOverScene(size: self.size)
self.view?.presentScene(gameOverScene)
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
var timeSinceLastUpdate = currentTime - lastUpdateTimerInterval
lastUpdateTimerInterval = currentTime
if (timeSinceLastUpdate > 1){
timeSinceLastUpdate = 1/60
lastUpdateTimerInterval = currentTime
}
updateWithTimeSinceLastUpdate(timeSinceLastUpdate)
}
}
here the GameOverScene file:
import UIKit
import SpriteKit
class GameOverScene: SKScene {
override init(size:CGSize){
super.init(size:size)
self.backgroundColor = SKColor.whiteColor()
var message:NSString = NSString()
message = "Game Over"
var label:SKLabelNode = SKLabelNode(fontNamed:"DamascusBold")
label.text = message
label.fontColor = SKColor.blackColor()
label.position = CGPointMake(self.size.width/2, self.size.height/2)
self.addChild(label)
var scene:GameScene!
self.runAction(SKAction.sequence([SKAction.waitForDuration(3.0),
SKAction.runBlock({
// var transition:SKTransition = SKTransition.flipHorizontalWithDuration(0.5)
var scene1:SKScene = GameScene(size: self.size)
self.view?.presentScene(scene1)
})
] ))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
gameviewcontroller file:
import UIKit
import SpriteKit
import AVFoundation
class GameViewController: UIViewController, UITextFieldDelegate{
var scene:GameScene!
func swipedRight(sender: UISwipeGestureRecognizer){
scene.swipedRight1(sender)
}
func swipedLeft(sender: UISwipeGestureRecognizer){
scene.swipedLeft1(sender)
}
func swipedDown(sender: UISwipeGestureRecognizer){
scene.swipedDown1(sender)
}
func swipedUp(sender: UISwipeGestureRecognizer){
scene.swipedUp1(sender)
}
override func viewDidLoad() {
super.viewDidLoad()
let skView = view as SKView
skView.multipleTouchEnabled = false
scene = GameScene(size: skView.bounds.size)
scene.scaleMode = SKSceneScaleMode.AspectFill
skView.presentScene(scene)
let swipeRight:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("swipedRight:"))
swipeRight.direction = .Right
view.addGestureRecognizer(swipeRight)
let swipeLeft:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("swipedLeft:"))
swipeLeft.direction = .Left
view.addGestureRecognizer(swipeLeft)
let swipeUp:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("swipedUp:"))
swipeUp.direction = .Up
view.addGestureRecognizer(swipeUp)
let swipeDown:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("swipedDown:"))
swipeDown.direction = .Down
view.addGestureRecognizer(swipeDown)
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
You're calling your swipedUp1 (etc) methods on the wrong scene. Here's what's happening:
Your view controller has a reference to the GameScene instance that you start with. Its gesture action methods call to that, and all is well.
When you start a new game from GameOverScene, that creates a new instance of GameScene and presents it in the view. Now you have two instances of GameScene: the one your view controller is still pointing to, and the one the view is now rendering.
When you're gesture actions fire, they're still talking to the first GameScene. So your log lines get printed, but you don't see anything happen because the second GameScene is the one being displayed.
You probably don't want two scenes sticking around, anyway.
You can fix both problems by eliminatimg the scene property in your view controller and having your gesture actions call through to view.scene instead (after appropriate casting):
func swipedUp(sender: UISwipeGestureRecognizer) {
let skView = view as SKView
let gameScene = skView.scene as GameScene
gameScene.swipedUp1(sender)
}
This way, the swipe goes to whichever scene the view controller's view is currently presenting.
Alternatively, you could keep your original swipedUp (etc) code, and change scene from a stored property to a read-only computed one that always gets you the currently presented scene:
var scene: GameScene {
let skView = view as SKView
return skView.scene as GameScene
}

Resources