Moving sprite with finger gesture - swift

I'm trying to move the sprite (red dot) whenever I move my finger anywhere on the screen of the device. The following code seems to work only within a small invisible circle rather than in the whole area of the screen, meaning the sprite seems to encounter some sort of wall and doesn't go any further. Anyone can figure out why?
import SpriteKit
import GameplayKit
var positionX = CGFloat()
var positionY = CGFloat()
var newPositionX = CGFloat()
var newPositionY = CGFloat()
var redDotPosition = CGPoint()
var oldPosition = CGPoint()
var newPosition = CGPoint()
class GameScene: SKScene {
var redDot : SKSpriteNode?
var entities = [GKEntity]()
var graphs = [String : GKGraph]()
private var lastUpdateTime : TimeInterval = 0
private var label : SKLabelNode?
private var spinnyNode : SKShapeNode?
override func sceneDidLoad() {
redDot = childNode(withName: "redDot") as? SKSpriteNode
redDot?.texture = SKTexture(imageNamed: "redDot")
redDot?.position = CGPoint(x: frame.midX , y: frame.midX)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchMoved(toPoint: t.location(in: self))
oldPosition = t.previousLocation(in: self)
let oldPositionX = oldPosition.x
let oldPositionY = oldPosition.y
newPositionX = t.location(in: self).x
newPositionY = t.location(in: self).y
deltaX = newPositionX - oldPositionX
deltaY = newPositionY - oldPositionY
redDot?.position = CGPoint(x: redDot!.position.x + deltaX , y: redDot!.position.y + deltaY)
}
}

Nevermind!
the problem lied in the fact that I had accidentally anchored my sprite to the scene, therefore it wasn't moving more than a certain amount.

Related

SpriteKit - Touch moved, how to prevent node jump to touch location

I wrote code for touch moved like this:
"override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {return}
let location = touch.location(in: self)
player.position = location"
The problem is the node/player will jump to wherever I touch and moved in the scene.
How may I fix this? Thank you.
I am going to answer this in pseudo code for two reasons, I am here to help you not do it for you and as a beginner figuring out how to do this yourself could be greatly beneficial.
var ball: SKSpriteNode!
var minWidth: CGFloat = 0
var maxWidth: CGFloat = 0
var startPosX: CGFloat = 0
var startBallPosX: CGFloat = 0
func didMove() {
//did move func of the scene
//setup ball and min and max widths
ball = SKSpriteNode()
addChild(ball)
minWidth = scene.frame.minX
maxWidth = scene.frame.maxY
}
func touchesBegan() {
let location = touch.location(in: self)
startPosX = location.x
startBallPosX = ball.position.x
}
func touchesMoved() {
let location = touch.location(in: self)
var moveXValue = location.x - startPosX
if startBallPosX + moveXValue > maxWidth {
//if ball wants to go past right edge of screen keep it at the edge
ball.position.x = maxWidth
}
else if startBallPosX + moveXValue < minWidth {
//if ball wants to go past left edge of screen keep it at the edge
ball.position.x = minWidth
}
else {
ball.position.x = startBallPosX + moveXValue
}
}

Game crash only on iPhone X and iPhone 8 Plus

I'm working on game and the game works perfect on all iPhones and iPad expect iPhone X and iPhone 8 Plus
When I add sprite to the scene the game crash (only on iPhone X and iPhone 8 Plus) because the sprite is already added to scene, On others iPhone the app not crash, I think the bug is in my timer, maybe the bug it's the iPhone simulator? What do you think?
Code:
class GameScene: SKScene, SKPhysicsContactDelegate {
var finger = SKSpriteNode(imageNamed: "Finger") //Finger to swipe the biscuit
var biscuit = SKSpriteNode(imageNamed: "Biscuit")
var glass = SKSpriteNode(imageNamed: "Glass")
var defaultbiscuitpos:CGPoint = CGPoint()
var defaultfingerpos:CGPoint = CGPoint()
/*** Drag Biscuit vars ***/
var touchpoint:CGPoint = CGPoint()
var istouching:Bool = false
var fadeoutfinger = SKAction()
var fadeinfinger = SKAction()
var fingergroup = SKAction()
var burnanimtion = SKAction()
var movefinger = SKAction()
let fingertimer:String = "fingertimer"
var isGameover:Bool = false
//Game mode enum
enum gamemode {
case dip
case ready
case out
case gameover
}
//Game mode (Dip,Ready,Out or game over by enum) **Now is Dip
var mymode = gamemode.dip
override func didMove(to view: SKView) {
//Finger
finger.name = "Finger"
finger.position = CGPoint(x: biscuit.position.x + finger.frame.width, y: biscuit.position.y)
defaultfingerpos = finger.position
finger.alpha = 1.0
finger.zPosition = 5
//Start finger timer to make animation
createTimer(name: fingeranimation, waitt: 3.0, sprite: finger, actioname: fingertimer)
}
//Finger timer func
func fingeranimation () {
//Check if timer is over 4 seconds and the title is dip
if mymode == gamemode.dip {
//Add finger to screen
addChild(finger)
//Set fade in animation for finger
fadeinfinger = SKAction.fadeIn(withDuration: 2.0)
//Set move animation for finger
movefinger = SKAction.moveTo(y: glass.frame.midX, duration: 2.0)
//Set fade out animation for finger
fadeoutfinger = SKAction.fadeOut(withDuration: 2.0)
fingergroup = SKAction.group([fadeinfinger,movefinger,fadeoutfinger])
finger.run(fingergroup, completion: {
//Remove finger from screen
self.finger.removeFromParent()
//Return the finger to apper and return the finger to default position
self.finger.alpha = 1.0
self.finger.position = self.defaultfingerpos
})
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first! as UITouch
let location = touch.location(in: self)
//Tap on biscuit
if biscuit.contains(location) && mymode != gamemode.gameover {
touchpoint = location
istouching = true
biscuit.physicsBody?.pinned = false
//Stop the finger animation timer
stopTimer(actioname: fingertimer, sprite: finger)
}
}
//Make timer function
func createTimer (name:#escaping os_block_t , waitt:TimeInterval, sprite:SKSpriteNode?,actioname: String) {
let myaction = SKAction.sequence([SKAction.wait(forDuration: waitt), SKAction.run(name)])
run(SKAction.repeatForever(myaction), withKey: actioname)
}
//Stop timer function
func stopTimer(actioname:String, sprite:SKSpriteNode?) {
removeAction(forKey: actioname)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if istouching && isGameover == false {
let dt:CGFloat = 1.0/15
let distance = CGVector(dx: touchpoint.x - biscuit.position.x, dy: touchpoint.y - biscuit.position.y * 1.65)
let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
self.biscuit.physicsBody!.velocity = velocity
}
}
}
This problem is now fixed with the release of Xcode 9.1.

How do I properly add a SKPhysicsBody to a drawn line using SKShapeNode in Spritekit?

I'm creating a game where the user must draw a line to collide with a ball to move it from one location to another. I just can't seem to find a way to put a physics body that collides with another object. The drawn line goes through other objects. I've tried several different SKPhysicBody init's on my shape, and none of them seem to work.
What's the best SKPhysicsBody initializer for a drawn line?
Are my category bit mask correct?
I understand the SkShapeNode can be converted to a SkSpriteNode, but I don't want to do this unless its the only way.
import SpriteKit
import UIKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var lines: [Line] = []
var lastTouch: CGPoint?
var touchLocation: CGPoint!
var linePhysics: CGPoint?
var ballCategory:UInt32 = 0b1
var lineCategory:UInt32 = 0b1 << 1
var goalCategory:UInt32 = 0b1 << 2
var floorCategory:UInt32 = 0b1 << 3
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
// assigns metal ball that was a color Sprite node, and is now a SKSpriteNode
let metalBall:SKSpriteNode = self.childNode(withName: "ball") as! SKSpriteNode
//assigns physical properties
metalBall.position = CGPoint(x: -150, y: 100)
metalBall.physicsBody? = SKPhysicsBody(circleOfRadius: metalBall.size.width / 2)
metalBall.physicsBody?.affectedByGravity = true
metalBall.physicsBody?.isDynamic = true
metalBall.physicsBody?.categoryBitMask = ballCategory
metalBall.physicsBody?.collisionBitMask = floorCategory | lineCategory
let floor: SKSpriteNode = self.childNode(withName: "floor") as! SKSpriteNode
floor.position = CGPoint (x: 0, y: -76)
floor.physicsBody = SKPhysicsBody(rectangleOf: floor.size)
floor.physicsBody?.affectedByGravity = false
floor.physicsBody?.isDynamic = false
floor.physicsBody?.categoryBitMask = floorCategory
floor.physicsBody?.collisionBitMask = ballCategory
}
/**
* Assigns the first touch location to a variable lastTouch
**/
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first {
lastTouch = firstTouch.location(in: self)
}
}
/**
* Assigns a path to where the finger touches then adds it to the line array and draws a path using shape nodes
**/
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first {
touchLocation = firstTouch.location(in: self)
//adds coordinates of lines to the line array
lines.append(Line(start: lastTouch!, end: touchLocation))
lastTouch = touchLocation
}
// path is a changeable variable that assigns a path depending on touch
let path = CGMutablePath()
for line in lines {
path.move(to: CGPoint(x: line.start.x, y: line.start.y))
path.addLine(to: CGPoint(x: line.end.x , y: line.end.y))
}
// colors in the path with ui color black
let shape = SKShapeNode()
shape.path = path
shape.strokeColor = UIColor.black
shape.lineWidth = 2
addChild(shape)
**shape.physicsBody = SKPhysicsBody(edgeChainFrom: shape.path!)**
shape.physicsBody?.affectedByGravity = true
shape.physicsBody?.isDynamic = false
shape.physicsBody?.friction = 1
shape.physicsBody?.restitution = 0.1
shape.physicsBody?.angularDamping = 0.0
shape.physicsBody?.linearDamping = 0
**shape.physicsBody?.categoryBitMask = lineCategory
shape.physicsBody?.collisionBitMask = ballCategory**
}
}

Swift creating physics joint

I am trying to work with SpriteKit and create an elbow joint that bends to allow movement of another joint. The problem is that when I create the joint, the parts all rotate 360 degrees as I swipe across the screen to action the movement, so there is no fixed joint that is kept (i.e. an elbow that has a range of movement). I have attached some of my code - some parts are commented out as I've been experimenting.
var lowerTorso: SKNode!
var upperTorso: SKNode!
var upperArmFront: SKNode!
var lowerArmFront: SKNode!
var upperArmBack: SKNode!
var lowerArmBack: SKNode!
var fistFront: SKNode!
var cricketBat: SKNode!
var bottom: SKNode!
let upperArmAngleDeg: CGFloat = 10
let lowerArmAngleDeg: CGFloat = 150
let batAngleDeg: CGFloat = 270
override func didMove(to view: SKView) {
physicsWorld.contactDelegate = self
physicsWorld.gravity = CGVector(dx: 0, dy: -20)
physicsWorld.speed = 0.1
//2
lowerTorso = childNode(withName: "torso_lower")
upperTorso = lowerTorso.childNode(withName: "torso_upper")
upperArmFront = upperTorso.childNode(withName: "arm_upper_front")
lowerArmFront = upperArmFront.childNode(withName: "arm_lower_front")
upperArmBack = upperArmFront.childNode(withName: "arm_upper_back")
lowerArmBack = upperArmBack.childNode(withName: "arm_lower_back")
cricketBat = lowerArmFront.childNode(withName: "cricketBat")
fistFront = cricketBat.childNode(withName: "fist_front")
let joint = SKPhysicsJointPin.joint(withBodyA: upperArmFront.physicsBody!,
bodyB: lowerArmFront.physicsBody!,
anchor: CGPoint(x: upperArmFront.frame.midX, y: upperArmFront.frame.midY))
physicsWorld.add(joint)
let joint2 = SKPhysicsJointPin.joint(withBodyA: lowerArmFront.physicsBody!,
bodyB: cricketBat.physicsBody!,
anchor: CGPoint(x: lowerArmFront.frame.maxX, y: lowerArmFront.frame.maxY))
physicsWorld.add(joint2)
/*
lowerArmFront.reachConstraints = SKReachConstraints(lowerAngleLimit: CGFloat(50).degreesToRadians(), upperAngleLimit: 160)
upperArmFront.reachConstraints = SKReachConstraints(lowerAngleLimit: CGFloat(-90).degreesToRadians(), upperAngleLimit: CGFloat(120).degreesToRadians())
lowerArmBack.reachConstraints = SKReachConstraints(lowerAngleLimit: CGFloat(50).degreesToRadians(), upperAngleLimit: 160)
upperArmBack.reachConstraints = SKReachConstraints(lowerAngleLimit: CGFloat(0).degreesToRadians(), upperAngleLimit: CGFloat(10).degreesToRadians())
*/
}
func punchAt(_ location: CGPoint) {
// 1
let punch = SKAction.reach(to: location, rootNode: upperArmFront, duration: 0.1)
/*
// 2
let restore = SKAction.run {
self.upperArmFront.run(SKAction.rotate(toAngle: self.upperArmAngleDeg.degreesToRadians(), duration: 0.1))
self.lowerArmFront.run(SKAction.rotate(toAngle: self.lowerArmAngleDeg.degreesToRadians(), duration: 0.1))
self.cricketBat.run(SKAction.rotate(toAngle: self.batAngleDeg.degreesToRadians(), duration: 0.1))
}
*/
fistFront.run(punch)
// 3
//fistFront.run(SKAction.sequence([punch, restore]))
}
// 3
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
touched = true
for touch: AnyObject in touches {
location = touch.location(in: self)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
location = touch.location(in: self)
}
}
According to the documentation here: https://developer.apple.com/reference/spritekit/skphysicsjointpin
you can set rotation limits on the pin joint via the joint's properties:
var shouldEnableLimits: Bool A Boolean value that indicates whether
the pin joint’s rotation is limited to a specific range of values.
var lowerAngleLimit: CGFloat The smallest angle allowed for the pin
joint, in radians.
var upperAngleLimit: CGFloat The largest angle allowed for the pin
joint, in radians.

Swift: Value Of A Variable Public But Still Local?

I change the value in a function and want to access the value in another function within the same class. My code:
class person{
var fieldsWidth = CGFloat()
var xPos = CGFloat()
var yPos = CGFloat()
var player = SKSpriteNode()
func addPlayer(gameScene: GameScene, x: CGFloat, y: CGFloat){
fieldsWidth = gameScene.screenWidth / CGFloat(hintergrund().fieldsX)
player = SKSpriteNode(imageNamed: "player")
player.size = CGSize(width: fieldsWidth, height: fieldsWidth)
player.position = CGPoint(x: x, y: y)
gameScene.addChild(player)
xPos = x
}
func move(gameScene: GameScene, point: CGPoint, fWidth: CGFloat){
print(xPos)
}
}
and the GameScene:
class GameScene: SKScene {
var screenWidth = CGFloat()
var screenHeight = CGFloat()
var touchLocation = CGFloat()
var fieldsWidth = CGFloat()
let player = person()
override func didMoveToView(view: SKView) {
screenWidth = self.size.width
screenHeight = self.size.height
fieldsWidth = screenWidth / CGFloat(hintergrund().fieldsX)
person().addPlayer(self, x: fieldsWidth/2, y: fieldsWidth/2)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
let touch = touches.first
var point = CGPoint()
point = touch!.locationInNode(self)
player.move(self, point: point, fWidth: fieldsWidth)
}
}
Somehow the value of xPos is just local in addPlayer. How can I access the value from xPos correctly? Hopefully you can help me.
The following code runs exactly as expected for me in a XCode Version 7.1 (7B91b) playground
class person{
var yPos = CGFloat()
func function1(y: CGFloat){
yPos = y
print(yPos) //Value of yPos is 31.25; correct!
}
func function2(){
print(yPos) //Value of yPos is 0.0; wrong!??
}
}
let foo = person()
foo.function1(31.25)
foo.function2() //Outputs 31.25
I think CGFloat is 0.0 by default so the most likely cause of your error is that you call function 2 before function one in your code. The error is somewhere else.
EDIT since you posted code. I would try this (create a new player and retain it -- I am not a sprite kit expert):
class GameScene: SKScene {
var screenWidth = CGFloat()
var screenHeight = CGFloat()
var touchLocation = CGFloat()
var fieldsWidth = CGFloat()
var player = person() // SEE HERE - Variable, so that you can reassign it
override func didMoveToView(view: SKView) {
screenWidth = self.size.width
screenHeight = self.size.height
fieldsWidth = screenWidth / CGFloat(hintergrund().fieldsX)
player = person().addPlayer(self, x: fieldsWidth/2, y: fieldsWidth/2) // SEE HERE - Create new player and retain it into your scene
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
let touch = touches.first
var point = CGPoint()
point = touch!.locationInNode(self)
player.move(self, point: point, fWidth: fieldsWidth)
}
}
PS: The above might work but really, from what I understand you need to create a player each time didMoveToView is called. If that is the case, then instead of doing this contrived addPlayer funciton, I would make a designated initialiser for the player class. Something like (untested):
func init(scene: SKScene, x: CGFLoat, y: CGFloat) {
// ETC...
}