swift SceneKit decline node move - swift

I create a sphere node, I need the user to be able only to rotate (left / right, up / down) and zoom in / out the node, but default he can move the node from the center (with two fingers) - is possible prohibit the user to move the node from the center? thanks for any help
sceneView.scene = scene
cameraOrbit = SCNNode()
cameraNode = SCNNode()
camera = SCNCamera()
// camera stuff
camera.usesOrthographicProjection = true
camera.orthographicScale = 5
camera.zNear = 1
camera.zFar = 100
cameraNode.position = SCNVector3(x: 0, y: 0, z: 70)
cameraNode.camera = camera
cameraOrbit = SCNNode()
cameraOrbit.addChildNode(cameraNode)
scene.rootNode.addChildNode(cameraNode)
let sphere = SCNSphere(radius: 2)
sphere.firstMaterial?.diffuse.contents = UIColor.red
let earthNode = SCNNode(geometry: sphere)
earthNode.name = "sphere"
earthNode.geometry?.materials = [blueMaterial]
scene.rootNode.addChildNode(earthNode)
earthNode.rotation = SCNVector4(0, 1, 0, 0)
let lightNode = SCNNode()
let light = SCNLight()
light.type = .ambient
light.intensity = 200
lightNode.light = light
scene.rootNode.addChildNode(lightNode)
sceneView.allowsCameraControl = true
sceneView.backgroundColor = UIColor.clear
sceneView.cameraControlConfiguration.allowsTranslation = true
sceneView.cameraControlConfiguration.rotationSensitivity = 0.4

You can put similar code into your UIViewController:
//**************************************************************************
// Gesture Recognizers
// MARK: Gesture Recognizers
//**************************************************************************
#objc func handleTap(recognizer: UITapGestureRecognizer)
{
if(data.isNavigationOff == true) { return } // No panel select if Add, Update, EndWave, or EndGame
if(gameMenuTableView.isHidden == false) { return } // No panel if game menu is showing
let location: CGPoint = recognizer.location(in: gameScene)
if(data.isAirStrikeModeOn == true)
{
let projectedPoint = gameScene.projectPoint(SCNVector3(0, 0, 0))
let scenePoint = gameScene.unprojectPoint(SCNVector3(location.x, location.y, CGFloat(projectedPoint.z)))
gameControl.airStrike(position: scenePoint)
}
else
{
let hitResults = gameScene.hitTest(location, options: hitTestOptions)
for vHit in hitResults
{
if(vHit.node.name?.prefix(5) == "Panel")
{
// May have selected an invalid panel or auto upgrade was on
if(gameControl.selectPanel(vPanel: vHit.node.name!) == false) { return }
return
}
}
}
}
//**************************************************************************
#objc func handlePan(recognizer: UIPanGestureRecognizer)
{
if(data.gameState != .run || data.isGamePaused == true) { return }
currentLocation = recognizer.location(in: gameScene)
switch recognizer.state
{
case UIGestureRecognizer.State.began:
beginLocation = recognizer.location(in: gameScene)
break
case UIGestureRecognizer.State.changed:
if(currentLocation.x > beginLocation.x * 1.1)
{
beginLocation.x = currentLocation.x
gNodes.camera.strafeLeft()
}
if(currentLocation.x < beginLocation.x * 0.9)
{
beginLocation.x = currentLocation.x
gNodes.camera.strafeRight()
}
break
case UIGestureRecognizer.State.ended:
break
default:
break
}
}

Yes, all of that is doable. First, create your own camera class and turn off allowsCameraControl. Then you can implement zoom/strafe/whatever.
Here are some examples that may help, just search for these numbers in the stack search bar and find my answers/examples.
57018359 - this post one tells you how to touch a 2d screen (tap) and translate it to 3d coordinates with you deciding the depth (z), like if you wanted to tap the screen and place an object in 3d space.
57003908 - this post tells you how to select an object with a hitTest (tap). For example, if you showed the front of a house with a door and tap it, then the function would return your door node provided you name the node "door" and took some kind of action when it's touched. Then you could reposition your camera based on that position. You'll want to go iterate through all results because there might be overlapping or plus Z nodes
55129224 - this post gives you quick example of creating a camera class. You can use this to reposition your camera or move it forward and back, etc.
Two finger drag:
func dragBegins(vRecognizer: UIPanGestureRecognizer)
{
if(data.gameState == .run)
{
if(vRecognizer.numberOfTouches == 2) { dragMode = .strafe }
}
}
class Camera
{
var data = Data.sharedInstance
var util = Util.sharedInstance
var gameDefaults = Defaults()
var cameraEye = SCNNode()
var cameraFocus = SCNNode()
var centerX: Int = 100
var strafeDelta: Float = 0.8
var zoomLevel: Int = 35
var zoomLevelMax: Int = 35 // Max number of zoom levels
//********************************************************************
init()
{
cameraEye.name = "Camera Eye"
cameraFocus.name = "Camera Focus"
cameraFocus.isHidden = true
cameraFocus.position = SCNVector3(x: 0, y: 0, z: 0)
cameraEye.camera = SCNCamera()
cameraEye.constraints = []
cameraEye.position = SCNVector3(x: 0, y: 15, z: 0.1)
let vConstraint = SCNLookAtConstraint(target: cameraFocus)
vConstraint.isGimbalLockEnabled = true
cameraEye.constraints = [vConstraint]
}
//********************************************************************
func reset()
{
centerX = 100
cameraFocus.position = SCNVector3(x: 0, y: 0, z: 0)
cameraEye.constraints = []
cameraEye.position = SCNVector3(x: 0, y: 32, z: 0.1)
cameraFocus.position = SCNVector3Make(0, 0, 0)
let vConstraint = SCNLookAtConstraint(target: cameraFocus)
vConstraint.isGimbalLockEnabled = true
cameraEye.constraints = [vConstraint]
}
//********************************************************************
func strafeRight()
{
if(centerX + 1 < 112)
{
centerX += 1
cameraEye.position.x += strafeDelta
cameraFocus.position.x += strafeDelta
}
}
//********************************************************************
func strafeLeft()
{
if(centerX - 1 > 90)
{
centerX -= 1
cameraEye.position.x -= strafeDelta
cameraFocus.position.x -= strafeDelta
}
}
//********************************************************************
}
//********************************************************************
func lerp(start: SCNVector3, end: SCNVector3, percent: Float) -> SCNVector3
{
let v3 = cgVecSub(v1: end, v2: start)
let v4 = cgVecScalarMult(v: v3, s: percent)
return cgVecAdd(v1: start, v2: v4)
}

Related

SKAction not running when button is held

I am working/practicing with SpriteKit and making a directional pad for controls. It works to move the character, except for the SKAction. I have the actions in the Actions.sks file that based off what button is pressed determines the action that is called. When I press the button the character moves fine, but when I hold it the character glides and the walking animation is stuck on the first frame until I release. What I am trying to do is have the character move if the button is pressed, and continue moving(with walking animation) when the button is held. I am trying to make it look like the gameboy era games.
class GameScene: SKScene {
var player = SKSpriteNode()
var playerSpeed: CGFloat = 0
var previousTimeInterval:TimeInterval = 0
let buttonNorth = SKSpriteNode(imageNamed: "Directional_Button")
let buttonSouth = SKSpriteNode(imageNamed: "Directional_Button")
let buttonEast = SKSpriteNode(imageNamed: "Directional_Button2")
let buttonWest = SKSpriteNode(imageNamed: "Directional_Button2")
var moveAtEndOfRelease:Bool = true
var isPressing = false
var currentState = MoveStates.n
override func didMove(to view: SKView) {
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
// Make sure you can get teh player from the scene file
if let somePlayer = self.childNode(withName: "player") as? SKSpriteNode {
player = somePlayer
// Set physics
player.physicsBody?.isDynamic = false
} else {
print("No player")
}
let widthHalf:CGFloat = self.view!.bounds.width / 2
let heightHalf:CGFloat = self.view!.bounds.height / 2
self.addChild(buttonNorth)
buttonNorth.position = CGPoint(x: -widthHalf + 80, y: -heightHalf + 100)
self.addChild(buttonSouth)
buttonSouth.position = CGPoint(x: -widthHalf + 80, y: -heightHalf + 40)
buttonSouth.yScale = -1
self.addChild(buttonWest)
buttonWest.position = CGPoint( x: -widthHalf + 30, y: -heightHalf + 70)
self.addChild(buttonEast)
buttonEast.position = CGPoint( x: -widthHalf + 130, y: -heightHalf + 70)
buttonNorth.xScale = 0.4
buttonNorth.yScale = 0.4
buttonSouth.xScale = 0.4
buttonSouth.yScale = 0.4
buttonSouth.zRotation = CGFloat(Double.pi)
buttonEast.xScale = 0.4
buttonEast.yScale = 0.4
buttonEast.zRotation = CGFloat(Double.pi)
buttonWest.xScale = 0.4
buttonWest.yScale = 0.4
}
override func update(_ currentTime: TimeInterval) {
//player.position = CGPoint(x: player.position.x + playerSpeedx, y: player.position.y + playerSpeedy)
if (isPressing) {
moveOnRelease()
}
}
func move(facing: Facing, x: CGFloat, y: CGFloat) {
let walkAnimation = SKAction(named: "walk\(facing)")!
let moveAction = SKAction.moveBy(x: x, y: y, duration: 1)
let group = SKAction.group([walkAnimation, moveAction])
// Run the actions
player.run(group)
}
func moveSide(facing: Facing, x: CGFloat) {
let walkAnimation = SKAction(named: "walk\(facing)")!
let moveAction = SKAction.moveBy(x: x, y: 0, duration: 1)
let group = SKAction.group([walkAnimation, moveAction])
// Run the actions
player.run(group)
}
func moveUpDown(facing: Facing, y: CGFloat) {
let walkAnimation = SKAction(named: "walk\(facing)")!
let moveAction = SKAction.moveBy(x: 0, y: y, duration: 1)
let group = SKAction.group([walkAnimation, moveAction])
// Run the actions
player.run(group)
}
func moveOnRelease() {
switch (currentState) {
case .n:
moveUpDown(facing: .Back, y: 1)
case .s:
moveUpDown(facing: .Front, y: -1)
case .e:
moveSide(facing: .Right, x: 1)
case .w:
moveSide(facing: .Left, x: -1)
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches ) {
let location = touch.location(in: self)
if (buttonNorth.frame.contains(location)) {
currentState = MoveStates.n
buttonNorth.texture = SKTexture(imageNamed: "Directional_Button_Lit")
isPressing = true
} else if (buttonSouth.frame.contains(location)) {
currentState = MoveStates.s
buttonSouth.texture = SKTexture(imageNamed: "Directional_Button_Lit")
isPressing = true
} else if (buttonEast.frame.contains(location)) {
currentState = MoveStates.e
buttonEast.texture = SKTexture(imageNamed: "Directional_Button2_Lit")
isPressing = true
} else if (buttonWest.frame.contains(location)) {
currentState = MoveStates.w
buttonWest.texture = SKTexture(imageNamed: "Directional_Button2_Lit")
isPressing = true
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if (moveAtEndOfRelease == true) {
buttonNorth.texture = SKTexture(imageNamed: "Directional_Button")
buttonSouth.texture = SKTexture(imageNamed: "Directional_Button")
buttonEast.texture = SKTexture(imageNamed: "Directional_Button2")
buttonWest.texture = SKTexture(imageNamed: "Directional_Button2")
//moveOnRelease()
isPressing = false
}
}
}
The reason it is stuck on the first frame is because the action is being called on every update i believe. You could check to see if the animation is already running, if so do not run it again.
To do this, for each run action give it a name.
So player.run(group) could be
player.run(group, withKey: "moveside"). Then, you can check to see if the action is already running or not like this if (player.action(forKey: "moveside") == nil). Finally, add in code to remove all other actions (like current movement or animations).
Putting it altogether:
if (player.action(forKey: "moveside") == nil) {
player.removeAllActions()
player.run(group, withKey: "moveside")
}

Room texture isn't fully rendered when I move SCNCamera around SCNBox inside the virtual room using SceneKit

I have tried to create a virtual room using SceneKit and put into it a box.
Also I have created a separate node with camera called ‘cameraOrbitNode’ (camera locates between the wall of room and the box). The main goal is to move ‘cameraOrbitNode’ around the box inside the room. I implemented it via pan gesture recognizer.
import UIKit
import SceneKit
enum WallType {
case left
case right
case top
case bottom
case forward
case back
}
class ViewController: UIViewController {
//MARK: - Outlets
#IBOutlet fileprivate weak var sceneView: SCNView!
//MARK: - Properties
fileprivate let roomLength: CGFloat = 12
fileprivate let cubeLength: CGFloat = 1
fileprivate var cameraOrbitNode: SCNNode?
fileprivate let panModifier: CGFloat = 100
//MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
setupSceneView()
setupRoom()
setupCameraOrbitNode()
setupGestures()
}
func setupSceneView() {
sceneView.scene = SCNScene()
sceneView.autoenablesDefaultLighting = true
}
func setupRoom() {
let roomNode = SCNNode()
roomNode.position = SCNVector3(0, 0, 0)
for wallType in Array<WallType>([.left, .right, .top, .bottom, .forward, .back]) {
roomNode.addChildNode(getWallNodeWith(wallType: wallType))
}
roomNode.addChildNode(getCubeNode())
sceneView.scene?.rootNode.addChildNode(roomNode)
}
func getWallNodeWith(wallType: WallType) -> SCNNode {
let wallGeometrySize = getWallSize()
let wallGeometry = SCNPlane(width: wallGeometrySize.width, height: wallGeometrySize.height)
wallGeometry.firstMaterial?.diffuse.contents = UIColor.darkGray
let wallNode = SCNNode(geometry: wallGeometry)
wallNode.position = getWallPositionWith(wallType: wallType)
wallNode.eulerAngles = getWallEulerAnglesWith(wallType: wallType)
return wallNode
}
func getWallSize() -> CGSize {
return CGSize(width: roomLength, height: roomLength)
}
func getWallPositionWith(wallType: WallType) -> SCNVector3 {
switch wallType {
case .left:
return SCNVector3(-roomLength / 2, 0, 0)
case .right:
return SCNVector3(roomLength / 2, 0, 0)
case .forward:
return SCNVector3(0, 0, -roomLength / 2)
case .back:
return SCNVector3(0, 0, roomLength / 2)
case .top:
return SCNVector3(0, roomLength / 2, 0)
case .bottom:
return SCNVector3(0, -roomLength / 2, 0)
}
}
func getWallEulerAnglesWith(wallType: WallType) -> SCNVector3 {
switch wallType {
case .left, .right:
return SCNVector3(0, CGFloat.pi / 2, 0)
case .forward, .back:
return SCNVector3(0, 0, 0)
case .top, .bottom:
return SCNVector3(CGFloat.pi / 2, 0, 0)
}
}
func getCubeNode() -> SCNNode {
let cubeGeometry = SCNBox(width: cubeLength, height: cubeLength, length: cubeLength, chamferRadius: 0)
cubeGeometry.firstMaterial?.diffuse.contents = UIColor.blue
let cubeNode = SCNNode(geometry: cubeGeometry)
cubeNode.position = SCNVector3(0, 0, 0)
return cubeNode
}
func setupCameraOrbitNode() {
let camera = SCNCamera()
camera.zNear = 0
camera.zFar = Double(roomLength)
let cameraNode = SCNNode()
cameraNode.position = SCNVector3(0, 0, (roomLength + cubeLength) / 4)
cameraNode.camera = camera
cameraOrbitNode = SCNNode()
guard let cameraOrbitNode = cameraOrbitNode else { return }
cameraOrbitNode.addChildNode(cameraNode)
cameraOrbitNode.position = SCNVector3(0, 0, 0)
sceneView.scene?.rootNode.addChildNode(cameraOrbitNode)
}
#objc func actionPan(gesture: UIPanGestureRecognizer) {
let translation = gesture.velocity(in: gesture.view)
cameraOrbitNode?.eulerAngles.y -= Float(translation.x/CGFloat(panModifier)) * .pi / 180
cameraOrbitNode?.eulerAngles.x -= Float(translation.y/CGFloat(panModifier)) * .pi / 180
}
func setupGestures() {
sceneView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(actionPan(gesture:))))
}
}
But in some cases you can see transparent walls of the room (basically room texture isn't fully rendered when I move the camera around the box). I was changing some camera properties (zNear, zFar) but nothing helped. Please see the links on images attached below. Thanks a lot for any help.
https://www.dropbox.com/s/ihxjrsgjfzidoff/IMG_0187.PNG?dl=0
https://www.dropbox.com/s/wd23xepcir3wggk/IMG_0188.PNG?dl=0
set isDoubleSdided to true
node.geometry?.firstMaterial?.isDoubleSided = true

Nodes in a Scene from different layers will not interact

Apologies in advance as this Question involves allot of code.
I have a GameScene with multiple layers holding Nodes/SpriteNodes for HUD/Scores/ etc. One layer, LayerProjectile, holds a Weapon Entity.
Said Entity has both Sprite and Physics Components.
Problem is the Projectile nodes in the LayerProjectile do not interact with nodes in the main layer (worldLayer), of the GameScene.
All physics bodies are dynamic, and the GameScene has its PhysicWorldDelgate
class GamePlayMode: SGScene, SKPhysicsContactDelegate {...
// Weapon Enity (SGEntity is a GKEntity)
class ThrowWeapon : SGEntity {
var spriteComponent: SpriteComponent!
var physicsComponent: PhysicsComponent
init(position: CGPoint, size: CGSize, texture:SKTexture){
super.init();
//Initilse Compnemnets
spriteComponent = SpriteComponent(entity: self, texture: texture, size: size, position: position);
addComponent(spriteComponent);
physicsComponent = PhysicsComponent(entity: self, bodySize: CGSize(width: spriteComponent.node.size.width * 0.8, height: spriteComponent.node.size.height * 0.8), bodyShape: .square, rotation: true);
physicsComponent.setCategoryBitmask(ColliderType.Projectile.rawValue, dynamic: true);
physicsComponent.setPhysicsCollisions(ColliderType.None.rawValue);
physicsComponent.setPhysicsContacts( ColliderType.Destroyable.rawValue | ColliderType.Enemy.rawValue);
physicsComponent.setEffectedByGravity(false);
addComponent(physicsComponent);
//Final Steps of compnements
spriteComponent.node.physicsBody = physicsComponent.physicsBody;
spriteComponent.node.name = "weapon";
name = "weapon";
spriteComponent.node.zPosition = 150;
spriteComponent.node.userData = ["startX" : position.x];
// Handle Contacts
override func contactWith(entity: SGEntity) {
//If contact with Enity in GemeScene
switch entity.name {
case "destructableEntity":
//Remove from Scsene self.parent?.parent?.runAction(SKAction.playSoundFileNamed("110548__noisehag__comp-cover-02.wav", waitForCompletion: false))
if let spriteComponentDestructable = entity.componentForClass(SpriteComponent.self){
let pos = spriteComponentDestructable.node.position;
spriteComponent.node.removeFromParent();
spriteComponentDestructable.node.parent?.runAction(SKAction.playSoundFileNamed("110548__noisehag__comp-cover-02.wav", waitForCompletion: false));
spriteComponentDestructable.node.removeAllActions();
spriteComponentDestructable.node.removeFromParent();
emitterExplosion(pos);
}
// self.runAction(SKAction.sequence([SKAction.fadeAlphaTo(0.0, duration: 0.5), SKAction.removeFromParent()]))
break;
case "enemyEntity":
if let spriteComponentEnemy = entity.componentForClass(SpriteComponent.self){
let pos = spriteComponentEnemy.node.position;
spriteComponent.node.removeFromParent();
spriteComponentEnemy.node.parent?.runAction(SKAction.playSoundFileNamed("110548__noisehag__comp-cover-02.wav", waitForCompletion: false));
spriteComponentEnemy.node.removeAllActions();
spriteComponentEnemy.node.removeFromParent();
emitterExplosion(pos);
} break;
default:
//
break;
}
LayerProjectile, which inherits from Layer- a SKNode, and which handles all weapon entities added to GameScene
class LayerProjectiles: Layer {
override func updateNodes(delta:CFTimeInterval,childNumber:Int,childNode: SKNode) {
print("Player movenment direction: \(playerMovingRighToLeft)");
if playerMovingRighToLeft {
// player moving to right so throw weapon same direction
childNode.position = childNode.position + CGPoint(x: delta * -weaponSpeed, y: 0.0);
childNode.zRotation = childNode.zRotation - (CGFloat(delta) * 10);
} else{
childNode.position = childNode.position + CGPoint(x: delta * weaponSpeed, y: 0.0)
childNode.zRotation = childNode.zRotation - (CGFloat(delta) * 10)
}
// When arrow within this layer moves out of the screen remove it, and the reverse
if childNode.position.x > ((childNode.userData!["startX"] as! CGFloat) + (SKMSceneSize!.width * 1.2)) || childNode.position.x < ((childNode.userData!["startX"]as! CGFloat) - (SKMSceneSize!.width * 1.2)){
childNode.removeFromParent()
}
}
Adding Instance of LayerProjectile and worldLayer (holding all other nodes, some of which have catBitMask set to notify when contact with Weapon Entity), to the GameScene (an instance of GamePlayMode, a SGScene)
class GameSceneState: GKState {
unowned let gs: GamePlayMode
init(scene: GamePlayMode) {
self.gs = scene
}
}
gs.physicsWorld.contactDelegate = gs
gs.physicsWorld.gravity = CGVector(dx: 0.0, dy: -1.0)
//Layers
gs.layerProjectile = LayerProjectiles();
gs.worldLayer = TileLayer(levelIndex: gs.levelIndex, typeIndex: .setMain)
gs.backgroundLayer = SKNode()
gs.overlayGUI = SKNode()
gs.addChild(gs.worldLayer)
gs.addChild(gs.layerProjectile);// For throwing wepaons, made in GamePlayMode
And finally the physics/sprite component settings for a Destrutable entity, which is a child of worldLayer (added to the GaemScene along with LayerProjectile)
class Destructable : SGEntity{
var spriteComponent: SpriteComponent!
var animationComponent: AnimationComponent!
var physicsComponent: PhysicsComponent!
var scrollerComponent: FullControlComponent!
init(position: CGPoint, size: CGSize, texture:SKTexture){
super.init();
//Initilse Compnemnets
spriteComponent = SpriteComponent(entity: self, texture: texture, size: size, position: position);
addComponent(spriteComponent);
physicsComponent = PhysicsComponent(entity: self, bodySize: CGSize(width: spriteComponent.node.size.width * 0.8, height: spriteComponent.node.size.height * 0.8), bodyShape: .square, rotation: false);
physicsComponent.setCategoryBitmask(ColliderType.Destroyable.rawValue , dynamic: true);
physicsComponent.setPhysicsCollisions(ColliderType.Wall.rawValue | ColliderType.Player.rawValue | ColliderType.Destroyable.rawValue); // collides with so bpounces off
physicsComponent.setPhysicsContacts(ColliderType.Wall.rawValue | ColliderType.Player.rawValue | ColliderType.Projectile.rawValue);
physicsComponent.setEffectedByGravity(true);
addComponent(physicsComponent);
//Final Steps of compnements
spriteComponent.node.physicsBody = physicsComponent.physicsBody;
spriteComponent.node.name = "destructableEntity";
name = "destructableEntity";
spriteComponent.node.zPosition = 150;
//spriteComponent.node.userData = ["startX" : position.x];
}
// Handle contacts
override func contactWith(entity: SGEntity) {
//
if entity.name == "weapon"{
print("Crate hit Projectile");
}
}
Instantionation of the Weapon Projectile (when user hits throw button during game play) and Destructible Entities when initializing GameScene and adding all sprite nodes:
let throwWeapon = ThrowWeapon(position: CGPoint(x: self.spriteComponent.node.position.x, y: self.spriteComponent.node.position.y + (self.spriteComponent.node.size.height / 2)), size: CGSize(width: 9, height: 40), texture: textureAtlas.textureNamed("kunai"));
playerEnt.gameScene.layerProjectile.addChild(throwWeapon.spriteComponent.node);
// Destructible Entity
gs.worldLayer.enumerateChildNodesWithName("placeholder_Destructable") { (node, stop) -> Void in
let crate = Destructable(position: node.position, size: CGSize(width: 32, height: 32), texture: tileAtlasInstance.textureNamed("Crate"));
//crate.name = "placeholder_Destructable";
//crate.spriteComponent.node.zPosition = GameSettings.GameParams.zValues.zWorldFront;
self.gs.addEntity(crate, toLayer: self.gs.worldLayer)
}
Any input appreciated.
Added: Category bitmask enum for ColliderTypes:
enum ColliderType:UInt32 {
case Player = 0
case Destroyable = 0b1
case Wall = 0b10
case Collectable = 0b100 // get points
case EndLevel = 0b1000
case Projectile = 0b10000
case None = 0b100000
case KillZone = 0b1000000
case arrow = 0b10000000;
case fire = 0b100000000; // die
case blueFire = 0b1000000000; // die
case greenCrystal = 0b10000000000; //more points like collectables
case barrel1 = 0b100000000000; //die
case barrell2 = 0b1000000000000; //die
case swordPoints = 0b10000000000000; //points
case spikes = 0b100000000000000; //die
case Enemy = 0b1000000000000000; // see Eneemy Entity
// against arrow and fire
}
// The SGEnity class where func handling physics contacts for Enities are called and overridden in Entity (Weapon, Destructible) classes
class SGEntity: GKEntity {
var name = ""
func contactWith(entity:SGEntity) {
//Overridden by subclass
}
// PhyscisComponent
enum PhysicsBodyShape {
case square
case squareOffset;// so the player knowns where the tile physics body is
case circle
case topOutline
case bottomOutline
}
class PhysicsComponent: GKComponent {
var physicsBody = SKPhysicsBody()
init(entity: GKEntity, bodySize: CGSize, bodyShape: PhysicsBodyShape, rotation: Bool) {
switch bodyShape {
case.square:
physicsBody = SKPhysicsBody(rectangleOfSize: bodySize)
break
case.squareOffset:
physicsBody = SKPhysicsBody(rectangleOfSize: bodySize, center: CGPoint(x: 0, y: bodySize.height/2 + 2))
break
case .circle:
physicsBody = SKPhysicsBody(circleOfRadius: bodySize.width / 2)
break
case .topOutline:
physicsBody = SKPhysicsBody(edgeFromPoint: CGPoint(x: (bodySize.width/2) * -1, y: bodySize.height/2), toPoint: CGPoint(x: bodySize.width/2, y: bodySize.height/2))
break
case .bottomOutline:
physicsBody = SKPhysicsBody(edgeFromPoint: CGPoint(x: (bodySize.width/2) * -1, y: (bodySize.height/2) * -1), toPoint: CGPoint(x: bodySize.width/2, y: (bodySize.height/2) * -1))
break
}
physicsBody.allowsRotation = rotation
//Defaults
physicsBody.dynamic = true
physicsBody.contactTestBitMask = ColliderType.None.rawValue
physicsBody.collisionBitMask = ColliderType.None.rawValue
}
func setCategoryBitmask(bitmask:UInt32, dynamic: Bool) {
physicsBody.categoryBitMask = bitmask
physicsBody.dynamic = dynamic
}
func setPhysicsCollisions(bitmask:UInt32) {
physicsBody.collisionBitMask = bitmask
}
func setPhysicsContacts(bitmask:UInt32) {
physicsBody.contactTestBitMask = bitmask
}
func setEffectedByGravity (gravity: Bool){
physicsBody.affectedByGravity = gravity;
}
}
So, contactsBegan is called via PhysicsDelage of the GameScene, :
if let bodyA = contact.bodyA.node as? EntityNode, //SpriteNode
let bodyAent = bodyA.entity as? SGEntity, // COnverted to SGEntity
let bodyB = contact.bodyB.node as? EntityNode, //SprtiteNode
let bodyBent = bodyB.entity as? SGEntity //ERROR HERE : Cast to SGEnity
{
print("SGENity Description: \(bodyBent)");
contactBegan(bodyAent, nodeB: bodyBent)
contactBegan(bodyBent, nodeB: bodyAent)
}else{
print("Cast error")
}
Issue appears to be casting the Projectile Entity to a SGEntity, this fails and contactsWith... of Projectile Entity is never called. All other SGEnities (Destructible) cast to SGEntity with no issues.,...

How to build a well working Overlay SK Panel(HUD) on a SCNScene

the app I'm working on is supposed to show a 3D object and the user can pick a color to color it. I have a SCNScene with multiple mesh creating a 3D model. I need to build a side interactive panel with colors the user can use to color the 3D model. The code is here on github.
I show you my code (for now on one class only, that's bad i know)
import UIKit
import QuartzCore
import SceneKit
import SpriteKit
class GameViewController: UIViewController {
var cameraOrbit = SCNNode()
let cameraNode = SCNNode()
let camera = SCNCamera()
let floorNode = SCNNode()
var wallNode = SCNNode()
var lateralWallRight = SCNNode()
var lateralWallLeft = SCNNode()
var spotLightNode = SCNNode()
//HANDLE PAN CAMERA
var initialPositionCamera = SCNVector3(x: -25, y: 70, z: 1450)
var translateEnabled = false
var lastXPos:Float = 0.0
var lastYPos:Float = 0.0
var xPos:Float = 0.0
var yPos:Float = 0.0
var lastWidthRatio: Float = 0
var lastHeightRatio: Float = 0.1
var widthRatio: Float = 0
var heightRatio: Float = 0.1
var fingersNeededToPan = 1 //change this from GUI
var panAttenuation: Float = 10 //5.0: very fast ---- 40.0 very slow
let maxWidthRatioRight: Float = 0.2
let maxWidthRatioLeft: Float = -0.2
let maxHeightRatioXDown: Float = 0.065
let maxHeightRatioXUp: Float = 0.4
//HANDLE PINCH CAMERA
var pinchAttenuation = 1.0 //1.0: very fast ---- 100.0 very slow
var lastFingersNumber = 0
let maxPinch = 146.0
let minPinch = 40.0
//OVERLAY
var colorPanelScene = SKScene()
var pickedColor: UIColor = UIColor.whiteColor()
var NodesToColors = [SKSpriteNode: UIColor]()
var didPickColor = false
var OverlayBackground: SKSpriteNode = SKSpriteNode()
func setColors() {
//Color Setup
let ColorWhite = colorPanelScene.childNodeWithName("ColorWhite") as! SKSpriteNode
let ColorRed = colorPanelScene.childNodeWithName("ColorRed") as! SKSpriteNode
let ColorBrown = colorPanelScene.childNodeWithName("ColorBrown")as! SKSpriteNode
let ColorDarkBrown = colorPanelScene.childNodeWithName("ColorDarkBrown")as! SKSpriteNode
let white = UIColor(red:1, green:0.95, blue:0.71, alpha:1)
let brown = UIColor(red:0.49, green:0.26, blue:0.17, alpha:1)
let red = UIColor(red:0.67, green:0.32, blue:0.21, alpha:1)
let darkBrown = UIColor(red:0.27, green:0.25, blue:0.21, alpha:1)
NodesToColors = [
ColorWhite: white,
ColorRed: red,
ColorBrown: brown,
ColorDarkBrown: darkBrown
]
OverlayBackground = colorPanelScene.childNodeWithName("OverlayBackground")as! SKSpriteNode
}
func blur(image image: UIImage) -> UIImage {
let radius: CGFloat = 20;
let context = CIContext(options: nil);
let inputImage = CIImage(CGImage: image.CGImage!);
let filter = CIFilter(name: "CIGaussianBlur");
filter?.setValue(inputImage, forKey: kCIInputImageKey);
filter?.setValue("\(radius)", forKey:kCIInputRadiusKey);
let result = filter?.valueForKey(kCIOutputImageKey) as! CIImage;
let rect = CGRectMake(radius * 2, radius * 2, image.size.width - radius * 4, image.size.height - radius * 4)
let cgImage = context.createCGImage(result, fromRect: rect);
let returnImage = UIImage(CGImage: cgImage);
return returnImage;
}
override func viewDidLoad() {
super.viewDidLoad()
// create a new scene
let scene = SCNScene(named: "art.scnassets/Figure.scn")!
// MARK: Lights
//create and add a light to the scene
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light!.type = SCNLightTypeOmni
lightNode.position = SCNVector3(x: 0, y: 1000, z: 1000)
scene.rootNode.addChildNode(lightNode)
// create and add an ambient light to the scene
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light!.type = SCNLightTypeAmbient
ambientLightNode.light!.color = UIColor.darkGrayColor()
scene.rootNode.addChildNode(ambientLightNode)
//MARK: Camera
camera.usesOrthographicProjection = true
camera.orthographicScale = 100
camera.zNear = 10
camera.zFar = 3000
cameraNode.position = initialPositionCamera
cameraNode.camera = camera
cameraOrbit = SCNNode()
cameraOrbit.addChildNode(cameraNode)
scene.rootNode.addChildNode(cameraOrbit)
//initial camera setup
self.cameraOrbit.eulerAngles.y = Float(-2 * M_PI) * lastWidthRatio
self.cameraOrbit.eulerAngles.x = Float(-M_PI) * lastHeightRatio
lastXPos = self.cameraNode.position.x
lastYPos = self.cameraNode.position.y
//MARK: Floor
let floor = SCNFloor()
floor.reflectionFalloffEnd = 0
floor.reflectivity = 0
floorNode.geometry = floor
floorNode.name = "Floor"
floorNode.geometry!.firstMaterial!.diffuse.contents = "art.scnassets/floor.png"
floorNode.geometry!.firstMaterial!.locksAmbientWithDiffuse = true
floorNode.geometry!.firstMaterial!.diffuse.wrapS = SCNWrapMode.Repeat
floorNode.geometry!.firstMaterial!.diffuse.wrapT = SCNWrapMode.Repeat
floorNode.geometry!.firstMaterial!.diffuse.mipFilter = SCNFilterMode.Nearest
floorNode.geometry!.firstMaterial!.doubleSided = false
floorNode.castsShadow = true
scene.rootNode.addChildNode(floorNode)
//MARK: Walls
// create the wall geometry
let wallGeometry = SCNPlane.init(width: 500.0, height: 300.0)
wallGeometry.firstMaterial!.diffuse.contents = "art.scnassets/background.jpg"
wallGeometry.firstMaterial!.diffuse.mipFilter = SCNFilterMode.Nearest
wallGeometry.firstMaterial!.diffuse.wrapS = SCNWrapMode.Repeat
wallGeometry.firstMaterial!.diffuse.wrapT = SCNWrapMode.Repeat
wallGeometry.firstMaterial!.doubleSided = false
wallGeometry.firstMaterial!.locksAmbientWithDiffuse = true
wallNode = SCNNode.init(geometry: wallGeometry)
wallNode.name = "FrontWall"
wallNode.position = SCNVector3Make(0, 120, -300) //this moves all 3 walls
wallNode.castsShadow = true
// RIGHT LATERAL WALL
lateralWallRight = SCNNode.init(geometry: wallGeometry)
lateralWallRight.name = "lateralWallRight"
lateralWallRight.position = SCNVector3Make(-300, -20, 150);
lateralWallRight.rotation = SCNVector4(x: 0, y: 1, z: 0, w: Float(M_PI/3))
lateralWallRight.castsShadow = true
wallNode.addChildNode(lateralWallRight)
// LEFT LATERAL WALL
lateralWallLeft = SCNNode.init(geometry: wallGeometry)
lateralWallLeft.name = "lateralWallLeft"
lateralWallLeft.position = SCNVector3Make(300, -20, 150);
lateralWallLeft.rotation = SCNVector4(x: 0, y: -1, z: 0, w: Float(M_PI/3))
lateralWallLeft.castsShadow = true
wallNode.addChildNode(lateralWallLeft)
//front walls
scene.rootNode.addChildNode(wallNode)
// 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 = false //not needed
// configure the view
scnView.backgroundColor = UIColor.grayColor()
//MARK: Gesture Recognizer in SceneView
// add a pan gesture recognizer
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(GameViewController.handlePan(_:)))
scnView.addGestureRecognizer(panGesture)
// add a tap gesture recognizer
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(GameViewController.handleTap(_:)))
scnView.addGestureRecognizer(tapGesture)
// add a pinch gesture recognizer
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(GameViewController.handlePinch(_:)))
scnView.addGestureRecognizer(pinchGesture)
//MARK: OverLay
colorPanelScene = SKScene(fileNamed: "art.scnassets/ColorPanelScene")!
scnView.overlaySKScene = colorPanelScene
scnView.overlaySKScene!.userInteractionEnabled = true;
didPickColor = false
setColors()
//let OverlayBackground = colorPanelScene.childNodeWithName("OverlayBackground")as! SKSpriteNode
}
func handlePan(gestureRecognize: UIPanGestureRecognizer) {
let numberOfTouches = gestureRecognize.numberOfTouches()
let translation = gestureRecognize.translationInView(gestureRecognize.view!)
if (numberOfTouches==fingersNeededToPan) {
widthRatio = Float(translation.x) / Float(gestureRecognize.view!.frame.size.width) + lastWidthRatio
heightRatio = Float(translation.y) / Float(gestureRecognize.view!.frame.size.height) + lastHeightRatio
// HEIGHT constraints
if (heightRatio >= maxHeightRatioXUp ) {
heightRatio = maxHeightRatioXUp
}
if (heightRatio <= maxHeightRatioXDown ) {
heightRatio = maxHeightRatioXDown
}
// WIDTH constraints
if(widthRatio >= maxWidthRatioRight) {
widthRatio = maxWidthRatioRight
}
if(widthRatio <= maxWidthRatioLeft) {
widthRatio = maxWidthRatioLeft
}
self.cameraOrbit.eulerAngles.y = Float(-2 * M_PI) * widthRatio
self.cameraOrbit.eulerAngles.x = Float(-M_PI) * heightRatio
lastFingersNumber = fingersNeededToPan
//TRANSLATION pan
} else if numberOfTouches == (fingersNeededToPan+1) {
if translateEnabled {
xPos = (lastXPos + Float(-translation.x))/(panAttenuation)
yPos = (lastYPos + Float(translation.y))/(panAttenuation)
self.cameraNode.position.x = xPos
self.cameraNode.position.y = yPos
}
lastFingersNumber = fingersNeededToPan+1
}
if (lastFingersNumber == fingersNeededToPan && numberOfTouches != fingersNeededToPan) {
lastWidthRatio = widthRatio
lastHeightRatio = heightRatio
}
if lastFingersNumber != (fingersNeededToPan+1) && numberOfTouches != (fingersNeededToPan+1) {
lastXPos = xPos
lastYPos = yPos
}
if (gestureRecognize.state == .Ended) {
if (lastFingersNumber==fingersNeededToPan) {
lastWidthRatio = widthRatio
lastHeightRatio = heightRatio
//print("lastHeight: \(round(lastHeightRatio*100))")
//print("lastWidth: \(round(lastWidthRatio*100))")
}
if lastFingersNumber==(fingersNeededToPan+1) {
lastXPos = xPos
lastYPos = yPos
print("lastX: \(xPos)")
print("lastY: \(yPos)")
}
print("Pan with \(lastFingersNumber) finger\(lastFingersNumber>1 ? "s" : "")")
}
}
func handlePinch(gestureRecognize: UIPinchGestureRecognizer) {
let pinchVelocity = Double.init(gestureRecognize.velocity)
//print("PinchVelocity \(pinchVelocity)")
camera.orthographicScale -= (pinchVelocity/pinchAttenuation)
if camera.orthographicScale <= minPinch {
camera.orthographicScale = minPinch
}
if camera.orthographicScale >= maxPinch {
camera.orthographicScale = maxPinch
}
if (gestureRecognize.state == .Ended) {
print("\nPinch: \(round(camera.orthographicScale))\n")
}
}
func handleTap(gestureRecognize: UIGestureRecognizer) {
print("---------------TAP-----------------")
// retrieve the SCNView
let scnView = self.view as! SCNView
let touchedPointInScene = gestureRecognize.locationInView(scnView)
let hitResults = scnView.hitTest(touchedPointInScene, options: nil)
let OverlayView = colorPanelScene.view! as SKView
let touchedPointInOverlay = gestureRecognize.locationInView(OverlayView)
// if button color are touched
if OverlayBackground.containsPoint(touchedPointInOverlay) {
print("OVERLAY: tap in \(touchedPointInOverlay)")
for (node, color) in NodesToColors {
// Check if the location of the touch is within the button's bounds
if node.containsPoint(touchedPointInOverlay) {
print("\(node.name!) -> color picked \(color.description)")
pickedColor = color
didPickColor = true
}
}
} else {//if figure is touched
// check that we clicked on at least one object
if hitResults.count > 0 && didPickColor {
// retrieved the first clicked object
let result: AnyObject! = hitResults[0]
print("OBJECT tap: \(result.node.name!)")
//Exclude floor and wall from color
if result.node! != floorNode && result.node! != wallNode && result.node! != lateralWallRight && result.node! != lateralWallLeft {
// get its material
let material = result.node!.geometry!.firstMaterial!
print("material: \(material.name!)")
// begin coloration
SCNTransaction.begin()
SCNTransaction.setAnimationDuration(0.5)
// on completion - keep color
SCNTransaction.setCompletionBlock {
SCNTransaction.begin()
SCNTransaction.setAnimationDuration(0.3)
material.diffuse.contents = self.pickedColor
SCNTransaction.commit()
}
SCNTransaction.commit()
material.diffuse.contents = pickedColor
}
}
}
print("-----------------------------------\n")
}
override func prefersStatusBarHidden() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return .Landscape
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
The code starts with a setColor function that catch images from ColorPanelScene.sks (this SKScene has a strange y-axis movement, i don't know why).
func setColors() {
//Color Setup
let ColorWhite = colorPanelScene.childNodeWithName("ColorWhite") as! SKSpriteNode
let ColorRed = colorPanelScene.childNodeWithName("ColorRed") as! SKSpriteNode
let ColorBrown = colorPanelScene.childNodeWithName("ColorBrown")as! SKSpriteNode
let ColorDarkBrown = colorPanelScene.childNodeWithName("ColorDarkBrown")as! SKSpriteNode
let white = UIColor(red:1, green:0.95, blue:0.71, alpha:1)
let brown = UIColor(red:0.49, green:0.26, blue:0.17, alpha:1)
let red = UIColor(red:0.67, green:0.32, blue:0.21, alpha:1)
let darkBrown = UIColor(red:0.27, green:0.25, blue:0.21, alpha:1)
NodesToColors = [
ColorWhite: white,
ColorRed: red,
ColorBrown: brown,
ColorDarkBrown: darkBrown
]
OverlayBackground = colorPanelScene.childNodeWithName("OverlayBackground")as! SKSpriteNode
}
Then, you can see a blur effect function that I would like to add to the panel background. Do you know how to do it to a SKNode? That would be easy if I use UIView instead, but i don't know how to back layer Views.
func blur(image image: UIImage) -> UIImage {
let radius: CGFloat = 20;
let context = CIContext(options: nil);
let inputImage = CIImage(CGImage: image.CGImage!);
let filter = CIFilter(name: "CIGaussianBlur");
filter?.setValue(inputImage, forKey: kCIInputImageKey);
filter?.setValue("\(radius)", forKey:kCIInputRadiusKey);
let result = filter?.valueForKey(kCIOutputImageKey) as! CIImage;
let rect = CGRectMake(radius * 2, radius * 2, image.size.width - radius * 4, image.size.height - radius * 4)
let cgImage = context.createCGImage(result, fromRect: rect);
let returnImage = UIImage(CGImage: cgImage);
return returnImage;
}
If you look at the buttons on ColorPanelScene.sks they have wrong names because I used a workaround to make that panel works. It seems to match color nodes, textures and nodes names in a inverse way.
That's obviously a bad implementation of a side panel. Please, can you help me to build a better interactive panel? Thank You.

moving pacman in swift

i am brand new to swift and i am trying to program a pacman. i am trying to move the pacman to the direction of the swipe, so far i have managed to move it to the edges of the screen, the problem is that when i try to move it not from the edge of the screen but in the middle of the swipe action, it just goes to the edge of the screen and moves to the swipe direction, here is the code for one direction:
var x = view.center.x
for var i = x; i > 17; i--
{
var origin: CGPoint = self.view.center
var move = CABasicAnimation(keyPath:"position.x")
move.speed = 0.13
move.fromValue = NSValue(nonretainedObject: view.center.x)
move.toValue = NSValue(nonretainedObject: i)
view.layer.addAnimation(move, forKey: "position")
view.center.x = i
}
the thing is that i know the problem which is when i swipe to the direction that i want the for loop will not wait for the animation to stop but it will finish the loop in less than a second and i need sort of delay here or other code.
This was an interesting question, so I decided to make an example in SpriteKit. There isn't any collision detection, path finding or indeed even paths. It is merely an example of how to make 'Pac-Man' change direction when a swipe occurs.
I have included the GameScene below:
class GameScene: SKScene {
enum Direction {
case Left
case Right
case Up
case Down
}
lazy var openDirectionPaths = [Direction: UIBezierPath]()
lazy var closedDirectionPaths = [Direction: UIBezierPath]()
lazy var wasClosedPath = false
lazy var needsToUpdateDirection = false
lazy var direction = Direction.Right
lazy var lastChange: NSTimeInterval = NSDate().timeIntervalSince1970
var touchBeganPoint: CGPoint?
let pacmanSprite = SKShapeNode(circleOfRadius: 15)
override func didMoveToView(view: SKView) {
let radius: CGFloat = 15, diameter: CGFloat = 30, center = CGPoint(x:radius, y:radius)
func createPaths(startDegrees: CGFloat, endDegrees: CGFloat, inout dictionary dic: [Direction: UIBezierPath]) {
var path = UIBezierPath(arcCenter: center, radius: radius, startAngle: startDegrees.toRadians(), endAngle: endDegrees.toRadians(), clockwise: true)
path.addLineToPoint(center)
path.closePath()
dic[.Right] = path
for d: Direction in [.Up, .Left, .Down] {
path = path.pathByRotating(90)
dic[d] = path
}
}
createPaths(35, 315, dictionary: &openDirectionPaths)
createPaths(1, 359, dictionary: &closedDirectionPaths)
pacmanSprite.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
pacmanSprite.fillColor = UIColor.yellowColor()
pacmanSprite.lineWidth = 2
if let path = openDirectionPaths[.Right] {
pacmanSprite.path = path.CGPath
}
pacmanSprite.strokeColor = UIColor.blackColor()
self.addChild(pacmanSprite)
updateDirection()
// Blocks to stop 'Pacman' changing direction outside of a defined path?
//375/25 = 15 width
//666/37 = 18 height
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
touchBeganPoint = positionOfTouch(inTouches: touches)
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
if let touchStartPoint = touchBeganPoint,
touchEndPoint = positionOfTouch(inTouches: touches) {
if touchStartPoint == touchEndPoint {
return
}
let degrees = atan2(touchStartPoint.x - touchEndPoint.x,
touchStartPoint.y - touchEndPoint.y).toDegrees()
var oldDirection = direction
switch Int(degrees) {
case -135...(-45): direction = .Right
case -45...45: direction = .Down
case 45...135: direction = .Left
default: direction = .Up
}
if (oldDirection != direction) {
needsToUpdateDirection = true
}
}
}
override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {
touchBeganPoint = nil
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if let nodes = self.children as? [SKShapeNode] {
for node in nodes {
let p = node.position
let s = node.frame.size
//let s = node.size
if p.x - s.width > self.size.width {
node.position.x = -s.width
}
if p.y - s.height > self.size.height {
node.position.y = -s.height
}
if p.x < -s.width {
node.position.x = self.size.width + (s.width / 2)
}
if p.y < -s.height {
node.position.y = self.size.height + (s.height / 2)
}
if needsToUpdateDirection || NSDate().timeIntervalSince1970 - lastChange > 0.25 {
if let path = wasClosedPath ? openDirectionPaths[direction]?.CGPath : closedDirectionPaths[direction]?.CGPath {
node.path = path
}
wasClosedPath = !wasClosedPath
lastChange = NSDate().timeIntervalSince1970
}
updateDirection()
}
}
}
// MARK:- Helpers
func positionOfTouch(inTouches touches: Set<NSObject>) -> CGPoint? {
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
return location
}
return nil
}
func updateDirection() {
if !needsToUpdateDirection {
return
}
pacmanSprite.removeActionForKey("Move")
func actionForDirection() -> SKAction {
let Delta: CGFloat = 25
switch (direction) {
case .Up:
return SKAction.moveByX(0.0, y: Delta, duration: 0.1)
case .Down:
return SKAction.moveByX(0.0, y: -Delta, duration: 0.1)
case .Right:
return SKAction.moveByX(Delta, y: 0.0, duration: 0.1)
default:
return SKAction.moveByX(-Delta, y: 0.0, duration: 0.1)
}
}
let action = SKAction.repeatActionForever(actionForDirection())
pacmanSprite.runAction(action, withKey: "Move")
needsToUpdateDirection = false
}
}
The repository can be found here
I have added the MIT license, so you can fork this repository if you wish. I hope this helps.