Move sprite connected with pin join by touch? - ios

I have a sprite node connected to another sprite node with a pin joint, anchored as such. I am trying to simulate this, where the green sprite is being moved with a finger:
https://i.stack.imgur.com/limMh.gif
This is what I have so far, but it's not rotating correctly:
body.name = "body"
body.size = CGSize(width: 10, height: 70)
body.physicsBody = SKPhysicsBody(rectangleOf: body.size)
body.physicsBody?.isDynamic = false
self.addChild(body)
leftArm.name = "leftArm"
leftArm.size = CGSize(width: 50, height: 50)
leftArm.position = CGPoint(x: -24, y: 0)
leftArm.physicsBody = SKPhysicsBody(rectangleOf: leftArm.size)
leftArm.physicsBody?.isDynamic = true
self.addChild(leftArm)
let leftArmBodyJoint = SKPhysicsJointPin.joint(withBodyA: leftArm.physicsBody!,
bodyB: body.physicsBody!,
anchor: CGPoint(x: self.body.frame.midX, y: self.body.frame.midY))
physicsWorld.add(leftArmBodyJoint)
let location = touches.first?.location(in: self)
let nodeTouched = atPoint(location!)
if nodeTouched.name == "leftArm" {
print("Began touch on left arm")
touchedLeftArm = nodeTouched
isFingerOnLeftArm = true
self.startPoint = touches.first!.location(in: self)
}
if isFingerOnLeftArm{
let newPoint = touches.first!.location(in: self)
let calculatedPoint = CGPoint(x: newPoint.x - startPoint.x, y: newPoint.y - startPoint.y)
leftArm.position = CGPoint(x: leftArm.position.x + calculatedPoint.x, y: leftArm.position.y + calculatedPoint.y)
let radians = atan2(newPoint.x - leftArm.position.x, newPoint.y - leftArm.position.y)
leftArm.zRotation = -radians//this rotates the player
self.startPoint = newPoint
}
Thanks for any help.

Related

SpriteKit SKTileMapNode same physics body for multiple tiles

The following code is correctly adding physicsBodies for every "wall" tile in my SKTileMapNode. However, I would like to group them all under the same node with one physicsBody for the entire group. How do I achieve that?
private func setupTileMap() {
let tileSize = self.tileMap.tileSize
let halfWidth = CGFloat(self.tileMap.numberOfColumns) / 2.0 * tileSize.width
let halfHeight = CGFloat(self.tileMap.numberOfRows) / 2.0 * tileSize.height
for column in 0..<self.tileMap.numberOfColumns {
for row in 0..<self.tileMap.numberOfRows {
let tileDefinition = self.tileMap.tileDefinition(atColumn: column, row: row)
let tileX = CGFloat(column) * tileSize.width - halfWidth
let tileY = CGFloat(row) * tileSize.height - halfHeight
let rect = CGRect(x: 0, y: 0, width: tileSize.width, height: tileSize.height)
let tileNode = SKShapeNode(rect: rect)
tileNode.strokeColor = .clear
tileNode.position = CGPoint(x: tileX, y: tileY)
let physicsBodyCenter = CGPoint(x: tileSize.width / 2.0, y: tileSize.height / 2.0)
let physicsBody = SKPhysicsBody(rectangleOf: tileSize, center: physicsBodyCenter)
physicsBody.isDynamic = false
tileNode.physicsBody = physicsBody
if tileDefinition?.name == Nodes.wall.rawValue {
physicsBody.categoryBitMask = CategoryMask.wall.rawValue
}
self.tileMap.addChild(tileNode)
}
}
}

Moving SKSpriteNode with touch, speeds up at half screen point

I'm working on a 2-d Platformer style game. I'm very new to IOS and Swift development. I'm attempting to use a button (a different node) to move my character across the screen from left to right. It works fine until I reach the halfway point then it speeds up dramatically and letting go of the touch of the button doesn't always stop it. Sometimes it requires another touch. Also the background doesn't appear to keep up with the player. Once the player hits mid screen the background should shift as the player continues moving.
I've pieced what I've done together from multiple tutorials SO examples but I'm stuck on this point.
class StoryModeScene: SKScene, SKPhysicsContactDelegate {
var tileMap = JSTileMap(named: "legend1Level1.tmx")
var tileSize:CGSize!
var xPointsToMovePerSecond:CGFloat = 0
var rightMoveButton = SKSpriteNode(imageNamed: "right-move")
var leftMoveButton = SKSpriteNode(imageNamed: "left-move")
var jumpButton = SKSpriteNode(imageNamed: "a-button")
var fireButton = SKSpriteNode(imageNamed: "b-button")
var forwardMarch:Bool = false
var mightAsWellJump:Bool = false
var onGround:Bool = true
//CREATE THE PLAYER ATLAS FOR ANIMATION
let playerAtlas = SKTextureAtlas(named:"legend1")
var playerSprites = Array<Any>()
var player = SKSpriteNode(imageNamed: "legend1")
var repeatActionPlayer = SKAction()
override func didMove(to view: SKView) {
/* Setup your scene here */
setupScene()
addPlayer()
//PREPARE TO ANIMATE THE PLAYER AND REPEAT THE ANIMATION FOREVER
let animatedPlayer = SKAction.animate(with: self.playerSprites as! [SKTexture], timePerFrame: 0.1)
self.repeatActionPlayer = SKAction.repeatForever(animatedPlayer)
leftMoveButton.position.x = 64
leftMoveButton.position.y = 64
leftMoveButton.name = "moveLeft"
addChild(leftMoveButton)
rightMoveButton.position.x = 124
rightMoveButton.position.y = 64
rightMoveButton.name = "moveRight"
addChild(rightMoveButton)
jumpButton.position.x = 771
jumpButton.position.y = 64
jumpButton.name = "jumpButton"
addChild(jumpButton)
fireButton.position.x = 836
fireButton.position.y = 64
fireButton.name = "fireButton"
addChild(fireButton)
}
override func update(_ currentTime: TimeInterval) {
if (forwardMarch) {
//let moveAction = SKAction.moveBy(x: 3, y: 0, duration: 1)
//let repeatForEver = SKAction.repeatForever(moveAction)
//let seq = SKAction.sequence([moveAction, repeatForEver])
//run the action on your ship
//player.run(seq)
player.position.x = player.position.x + 3
setViewpointCenter(player.position)
}
if (mightAsWellJump) {
let jumpForce = CGPoint(x: 0.0, y: 310.0)
let jumpCutoff: Float = 150.0
if mightAsWellJump && onGround {
player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx + jumpForce.x, dy: player.physicsBody!.velocity.dy + jumpForce.y)
onGround = false
} else if !mightAsWellJump && player.physicsBody!.velocity.dy > CGFloat(jumpCutoff) {
player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx, dy: CGFloat(jumpCutoff))
}
player.position = CGPoint(x: player.position.x, y: player.position.y + 5);
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches) {
let positionInScene = touch.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
if name == "jumpButton" {
mightAsWellJump = true
player.texture = SKTexture(imageNamed: "legend1_jump")
}
if name == "moveRight" {
forwardMarch = true
self.player.run(repeatActionPlayer)
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
if rightMoveButton.contains(touch.location(in: self)) {
forwardMarch = false
player.removeAllActions()
player.texture = SKTexture(imageNamed: "legend1")
}
if jumpButton.contains(touch.location(in: self)) {
mightAsWellJump = false
player.removeAllActions()
player.texture = SKTexture(imageNamed: "legend1")
}
}
func setViewpointCenter(_ position: CGPoint) {
var x = max(position.x, size.width / 2)
var y = max(position.y, size.height / 2)
x = min(x, (tileMap!.mapSize.width * tileMap!.tileSize.width) - size.width / 2)
y = min(y, (tileMap!.mapSize.height * tileMap!.tileSize.height) - size.height / 2)
let actualPosition = CGPoint(x: CGFloat(x), y: CGFloat(y))
let centerOfView = CGPoint(x: size.width / 2, y: size.height / 2)
let viewPoint = CGPoint(x: (centerOfView.x - actualPosition.x) * 3, y: centerOfView.y - actualPosition.y)
tileMap!.position = viewPoint
}
func setupScene() {
playerSprites.append(playerAtlas.textureNamed("legend1_0"))
playerSprites.append(playerAtlas.textureNamed("legend1_1"))
playerSprites.append(playerAtlas.textureNamed("legend1_2"))
playerSprites.append(playerAtlas.textureNamed("legend1_3"))
playerSprites.append(playerAtlas.textureNamed("legend1_4"))
playerSprites.append(playerAtlas.textureNamed("legend1_5"))
backgroundColor = UIColor(red: 165.0/255.0, green: 216.0/255.0, blue: 255.0/255.0, alpha: 1.0)
physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)
anchorPoint = CGPoint(x: 0, y: 0)
position = CGPoint(x: 0, y: 0)
let point = tileMap!.calculateAccumulatedFrame()
print (point)
tileMap!.position = CGPoint(x: 0, y: 0)
addChild(tileMap!)
addFloor()
}
func addFloor() {
for a in 0..<Int(tileMap!.mapSize.width) {
for b in 0..<Int(tileMap!.mapSize.height) {
let layerInfo:TMXLayerInfo = tileMap!.layers.firstObject as! TMXLayerInfo
let point = CGPoint(x: a, y: b)
let walls = tileMap!.layerNamed("Walls")
let wallInfo:TMXLayerInfo = walls!.layerInfo
let wallGIDs = wallInfo.layer.tileGid(at: wallInfo.layer.point(forCoord: point))
if wallGIDs > 0 {
//print (wallGIDs)
//let node = walls
let node = wallInfo.layer.tile(atCoord: point)
node!.physicsBody = SKPhysicsBody(rectangleOf: node!.size)
node!.physicsBody?.isDynamic = false
}
}
}
}
func addPlayer() {
tileSize = tileMap?.tileSize
player.position = CGPoint(x: tileSize.width + player.size.width/2, y: tileSize.height + player.size.height*8)
let rect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 50, height: 95))
player.physicsBody = SKPhysicsBody(rectangleOf: rect.size)
player.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)
player.physicsBody!.isDynamic = true
player.physicsBody!.restitution = 0
player.physicsBody!.allowsRotation = false
player.physicsBody!.friction = 1.0
addChild(player)
}
}
Holding the rightMoveButton should move at a consistent pace to the right. As the player gets to mid screen the view point of the background should shift until it reaches the end of the background at which point the player can move off screen and complete the level. Releasing the button should allow the player to stop.
You can move the scene as the character moves by creating an SKCameraNode. You then can choose when to move the camera to create the right affect. Make sure to set the camera as your game’s camera. You can learn about cameras here. As for the speeding up, I assume it has something to do with your physicsBody. If you’re interested about player movement, you could look here or here. There are many other great videos and sites online, which you could find by searching “SpriteKit player movement.”
Eli Front's answer pointed me in the right direction. I still have some issues with this code but the answer to my question was using SKCamera node as Eli pointed out. I just wanted to post the code that works so if anyone has a similar question there's a code example as far as camera moving with the player. The speed issue was essentially an optical illusion with the background moving too fast.
Note that the controls are children of the camera. That is so they move with the camera as well otherwise as your node moves the controls would move off screen.
import UIKit
import SpriteKit
import GameKit
class StoryModeScene: SKScene, SKPhysicsContactDelegate {
var tileMap = JSTileMap(named: "legend1Level1.tmx")
var tileSize:CGSize!
var xPointsToMovePerSecond:CGFloat = 0
var rightMoveButton = SKSpriteNode(imageNamed: "right-move")
var leftMoveButton = SKSpriteNode(imageNamed: "left-move")
var jumpButton = SKSpriteNode(imageNamed: "a-button")
var fireButton = SKSpriteNode(imageNamed: "b-button")
var forwardMarch:Bool = false
var mightAsWellJump:Bool = false
var onGround:Bool = true
//CREATE THE Player ATLAS FOR ANIMATION
let playerAtlas = SKTextureAtlas(named:"legend1")
var playerSprites = Array<Any>()
var player = SKSpriteNode(imageNamed: "legend1")
var repeatActionPlayer = SKAction()
let cam = SKCameraNode()
var previousUpdateTime: TimeInterval = 0
override func didMove(to view: SKView) {
// SETUP CAMERA
self.camera = cam
scene?.addChild(cam)
cam.position.x = 448
cam.position.y = 212
setupScene()
addPlayer()
//PREPARE TO ANIMATE THE PLAYER AND REPEAT THE ANIMATION FOREVER
let animatedPlayer = SKAction.animate(with: self.playerSprites as! [SKTexture], timePerFrame: 0.1)
self.repeatActionPlayer = SKAction.repeatForever(animatedPlayer)
// SETUP CONTROLS
leftMoveButton.position.x = -338
leftMoveButton.position.y = -112
leftMoveButton.name = "moveLeft"
leftMoveButton.zPosition = 5
cam.addChild(leftMoveButton)
rightMoveButton.position.x = -278
rightMoveButton.position.y = -112
rightMoveButton.name = "moveRight"
cam.addChild(rightMoveButton)
jumpButton.position.x = 278
jumpButton.position.y = -112
jumpButton.name = "jumpButton"
jumpButton.zPosition = 5
cam.addChild(jumpButton)
fireButton.position.x = 338
fireButton.position.y = -112
fireButton.name = "fireButton"
cam.addChild(fireButton)
}
override func update(_ currentTime: TimeInterval) {
if (forwardMarch) {
if player.position.x > 448 && player.position.x < 1800 {
cam.position.x = player.position.x
} else if player.position.x >= 1800 {
cam.position.x = 1800
}
setViewpointCenter(player.position)
player.position.x = player.position.x + 3
}
if (mightAsWellJump) {
let jumpForce = CGPoint(x: 0.0, y: 310.0)
let jumpCutoff: Float = 150.0
if mightAsWellJump && onGround {
player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx + jumpForce.x, dy: player.physicsBody!.velocity.dy + jumpForce.y)
onGround = false
} else if !mightAsWellJump && player.physicsBody!.velocity.dy > CGFloat(jumpCutoff) {
player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx, dy: CGFloat(jumpCutoff))
}
player.position = CGPoint(x: player.position.x, y: player.position.y + 5);
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches) {
let positionInScene = touch.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
if name == "jumpButton" {
mightAsWellJump = true
player.texture = SKTexture(imageNamed: "legend1_jump")
}
if name == "moveRight" {
player.physicsBody!.velocity.dy = 0.0
forwardMarch = true
self.player.run(repeatActionPlayer)
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches) {
let positionInScene = touch.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
if name == "jumpButton" {
mightAsWellJump = false
player.removeAllActions()
player.texture = SKTexture(imageNamed: "legend1")
player.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)
}
if name == "moveRight" {
player.removeAllActions()
player.texture = SKTexture(imageNamed: "legend1")
forwardMarch = false
}
}
}
}
func setViewpointCenter(_ position: CGPoint) {
var x = max(position.x, size.width / 2)
var y = max(position.y, size.height / 2)
x = min(x, (tileMap!.mapSize.width * tileMap!.tileSize.width) - size.width / 2)
y = min(y, (tileMap!.mapSize.height * tileMap!.tileSize.height) - size.height / 2)
let actualPosition = CGPoint(x: CGFloat(x), y: CGFloat(y))
let centerOfView = CGPoint(x: size.width / 2, y: size.height / 2)
let viewPoint = CGPoint(x: centerOfView.x - actualPosition.x, y: centerOfView.y - actualPosition.y)
if (actualPosition.x > 1800) {
tileMap!.position = tileMap!.position
} else {
tileMap!.position = viewPoint
}
}
func setupScene() {
// ADD PLAYER SPRITES #TODO MAKE THIS DYNAMIC
playerSprites.append(playerAtlas.textureNamed("legend1_0"))
playerSprites.append(playerAtlas.textureNamed("legend1_1"))
playerSprites.append(playerAtlas.textureNamed("legend1_2"))
playerSprites.append(playerAtlas.textureNamed("legend1_3"))
playerSprites.append(playerAtlas.textureNamed("legend1_4"))
playerSprites.append(playerAtlas.textureNamed("legend1_5"))
backgroundColor = UIColor(red: 165.0/255.0, green: 216.0/255.0, blue: 255.0/255.0, alpha: 1.0)
anchorPoint = CGPoint(x: 0, y: 0)
position = CGPoint(x: 0, y: 0)
// let point = tileMap!.calculateAccumulatedFrame()
tileMap!.position = CGPoint(x: 0, y: 0)
addChild(tileMap!)
addFloor()
}
func addFloor() {
for a in 0..<Int(tileMap!.mapSize.width) {
for b in 0..<Int(tileMap!.mapSize.height) {
// let layerInfo:TMXLayerInfo = tileMap!.layers.firstObject as! TMXLayerInfo
let point = CGPoint(x: a, y: b)
let walls = tileMap!.layerNamed("Walls")
let wallInfo:TMXLayerInfo = walls!.layerInfo
let wallGIDs = wallInfo.layer.tileGid(at: wallInfo.layer.point(forCoord: point))
if wallGIDs > 0 {
let node = wallInfo.layer.tile(atCoord: point)
node!.physicsBody = SKPhysicsBody(rectangleOf: node!.size)
node!.physicsBody?.isDynamic = false
}
}
}
}
func addPlayer() {
tileSize = tileMap?.tileSize
player.position = CGPoint(x: tileSize.width + player.size.width/2, y: tileSize.height + player.size.height*8)
let rect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 50, height: 95))
player.physicsBody = SKPhysicsBody(rectangleOf: rect.size)
player.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)
player.physicsBody!.isDynamic = true
player.physicsBody!.restitution = 0
player.physicsBody!.allowsRotation = false
player.physicsBody!.friction = 1.0
player.physicsBody!.mass = 1.0
addChild(player)
}
}
As I mentioned I still have some issues like the player node floating/flying when I jump/run at the same time. The 1st jump is the correct height and all subsequent jumps are smaller etc but I'm working on figuring those out.

How can I simulate shrink and stretch a rope - Swift

How can I simulate shrink and stretch a rope, I want to joint two sprite nodes and when the getting closer the rope should be stretched and shrunk once they go to the original position, I used a lot of way using Limit and Spring joints but I really don't know how to animate the rope to accomplish the shrink and stretch.
Attempt 1
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
self.addAnchor()
self.addRope()
self.addCharacter()
let jointOneFixed = SKPhysicsJointFixed.joint(withBodyA: anchor.physicsBody!, bodyB: rope.physicsBody!, anchor: anchor.position)
self.physicsWorld.add(jointOneFixed)
let jointTwoFixed = SKPhysicsJointFixed.joint(withBodyA: rope.physicsBody!, bodyB: currentCharacter.physicsBody!, anchor: CGPoint(x: rope.frame.midX, y: rope.frame.minY))
self.physicsWorld.add(jointTwoFixed)
limitJoint = SKPhysicsJointLimit.joint(withBodyA: anchor.physicsBody!, bodyB: currentCharacter.physicsBody!, anchorA: anchor.position, anchorB: currentCharacter.position)
//limitJoint.maxLength = self.size.height - 1000
self.physicsWorld.add(limitJoint)
}
func addAnchor(){
anchor.position = CGPoint(x: self.size.width / 2, y: self.size.height + 1)
anchor.anchorPoint = CGPoint(x: 0.5, y: 0.5)
anchor.setScale(1)
anchor.zPosition = 2
anchor.name = "anchor"
anchor.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: anchor.size.width, height: anchor.size.height))
anchor.physicsBody?.isDynamic = false
anchor.physicsBody?.affectedByGravity = false
anchor.physicsBody?.allowsRotation = true
self.addChild(anchor)
}
func addRope() {
rope.position = CGPoint(x: anchor.position.x, y: anchor.position.y - 175)
rope.setScale(0.65)
rope.zPosition = 1
rope.name = "rope"
rope.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: rope.size.width, height: rope.size.height))
rope.physicsBody?.affectedByGravity = false;
rope.physicsBody?.mass = 0.5;
rope.physicsBody?.allowsRotation = true
self.addChild(rope)
}
func addCharacter() {
let characterName: String = UserDefaults.standard.string(forKey: "current_character")!
currentCharacter = SKSpriteNode(imageNamed: characterName);
currentCharacter.position = CGPoint(x: self.size.width / 2, y: self.size.height - 400)
currentCharacter.anchorPoint = CGPoint(x: 0.5, y: 0.5)
currentCharacter.setScale(0.50)
currentCharacter.zPosition = 2
currentCharacter.name = "character"
currentCharacter.physicsBody = SKPhysicsBody(circleOfRadius: max(currentCharacter.size.width / 2, currentCharacter.size.height / 2))
currentCharacter.physicsBody?.affectedByGravity = false
currentCharacter.physicsBody?.isDynamic = true
currentCharacter.physicsBody?.allowsRotation = true
self.addChild(currentCharacter)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let moveDown = SKAction.moveBy(x: 0, y: -100, duration:0.1)
currentCharacter.run(moveDown)
}
Attempt 2
Is almost the same except instead of create a Limit joint I created a Spring joint
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
self.addAnchor()
self.addRope()
self.addCharacter()
let jointOneFixed = SKPhysicsJointFixed.joint(withBodyA: anchor.physicsBody!, bodyB: rope.physicsBody!, anchor: anchor.position)
self.physicsWorld.add(jointOneFixed)
let jointTwoFixed = SKPhysicsJointFixed.joint(withBodyA: rope.physicsBody!, bodyB: currentCharacter.physicsBody!, anchor: CGPoint(x: rope.frame.midX, y: rope.frame.minY))
self.physicsWorld.add(jointTwoFixed)
let springJoint = SKPhysicsJointSpring.joint(withBodyA: anchor.physicsBody!, bodyB: currentCharacter.physicsBody!, anchorA: anchor.position, anchorB: currentCharacter.position)
//springJoint.frequency = 1.0
//springJoint.damping = 0
self.physicsWorld.add(springJoint)
}

SpriteKit - Collision detection not working

i've got a problem with the collisions.
adding physicbody depending on tilemap:
guard let tilemap = childNode(withName: "LevelGround") as? SKTileMapNode else { return }
let tileSize = tilemap.tileSize
let halfWidth = CGFloat(tilemap.numberOfColumns) / 2.0 * tileSize.width
let halfHeight = CGFloat(tilemap.numberOfRows) / 2.0 * tileSize.height
for row in 0..<tilemap.numberOfRows {
for col in 0..<tilemap.numberOfColumns {
if tilemap.tileDefinition(atColumn: col, row: row) != nil {
let x = CGFloat(col) * tileSize.width - halfWidth
let y = CGFloat(row) * tileSize.height - halfHeight
let rect = CGRect(x: 0, y: 0, width: tileSize.width, height: tileSize.height)
let tileNode = SKShapeNode(rect: rect)
tileNode.position = CGPoint(x: x, y: y)
tileNode.physicsBody = SKPhysicsBody(rectangleOf: tileSize, center: CGPoint(x: tileSize.width / 2.0, y: tileSize.height / 2.0))
tileNode.physicsBody?.isDynamic = false
tileNode.physicsBody?.affectedByGravity = false
tileNode.physicsBody?.collisionBitMask = Physics().player | Physics().bullet
tileNode.physicsBody?.categoryBitMask = Physics().ground
tileNode.physicsBody?.contactTestBitMask = Physics().ground | Physics().player
tileNode.physicsBody?.friction = 0.1
tileNode.physicsBody?.restitution = 0
tileNode.strokeColor = .white
tileNode.name = "Ground"
self.world.addChild(tileNode)
}
}
}
Let the bullet spawn on tapping the action2 button:
if self.ctrlAction2.contains(touch.location(in: cam)) {
let bullet = SKShapeNode(ellipseOf: CGSize(width: 5, height: 4))
bullet.fillColor = .orange
bullet.physicsBody = SKPhysicsBody(edgeLoopFrom: bullet.path!)
bullet.physicsBody?.affectedByGravity = true
bullet.physicsBody?.categoryBitMask = Physics().bullet
bullet.physicsBody?.collisionBitMask = Physics().enemy | Physics().ground
bullet.physicsBody?.contactTestBitMask = Physics().bullet | Physics().enemy | Physics().ground
bullet.position.y = charakter.position.y + charakter.size.height / 7
bullet.physicsBody?.isDynamic = true
bullet.name = "bullet"
if facing == "right" {
bullet.position.x = charakter.position.x + charakter.size.width / 1.9
self.world.addChild(bullet)
bullet.run(SKAction.moveBy(x: 2000, y: 0, duration: 4))
} else if facing == "left" {
bullet.position.x = charakter.position.x - charakter.size.width / 1.9
self.world.addChild(bullet)
bullet.run(SKAction.moveBy(x: -2000, y: 0, duration: 4))
}
}
The bullet collides with enemies, the enemies collides with the player and the player collides with the ground - everything works, except the collision between bullet and ground.
Thank you for your help in advance
Pascal

Sprite Kit Won't stop adding nodes

In my project everything is going smoothly and working great except for when I want to add 1 instance of a SKLabelNode when a certain event happens in my game.
Then problem is when the event happens it adds the SKLabelNode more than once and it keeps on doing it into the thousands...
Here is my code:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate{
var isTouched: Bool = false
let lavaRoom = SKSpriteNode(imageNamed: "LavaRom")
let tileStone1 = SKSpriteNode(imageNamed: "TileStone")
let tileStone2 = SKSpriteNode(imageNamed: "TileStone")
let tileStone3 = SKSpriteNode(imageNamed: "TileStone")
let tileStone4 = SKSpriteNode(imageNamed: "TileStone")
let tileStone5 = SKSpriteNode(imageNamed: "TileStone")
let tileStone6 = SKSpriteNode(imageNamed: "TileStone")
let tileStone7 = SKSpriteNode(imageNamed: "TileStone")
let tileStone8 = SKSpriteNode(imageNamed: "TileStone")
let tileStone9 = SKSpriteNode(imageNamed: "TileStone")
let tileStone10 = SKSpriteNode(imageNamed: "TileStone")
let tileStone11 = SKSpriteNode(imageNamed: "TileStone")
let tileStone12 = SKSpriteNode(imageNamed: "TileStone")
let tileStone13 = SKSpriteNode(imageNamed: "TileStone")
let tileStone14 = SKSpriteNode(imageNamed: "TileStone")
let tileStone15 = SKSpriteNode(imageNamed: "TileStone")
let tileStone16 = SKSpriteNode(imageNamed: "TileStone")
let tileStone17 = SKSpriteNode(imageNamed: "TileStone")
let tileStone18 = SKSpriteNode(imageNamed: "TileStone")
let tileStone19 = SKSpriteNode(imageNamed: "TileStone")
let tileStone20 = SKSpriteNode(imageNamed: "TileStone")
let tileStone21 = SKSpriteNode(imageNamed: "TileStone")
let tileStone22 = SKSpriteNode(imageNamed: "TileStone")
let tileStone23 = SKSpriteNode(imageNamed: "TileStone")
let tileStone24 = SKSpriteNode(imageNamed: "TileStone")
let tileStone25 = SKSpriteNode(imageNamed: "TileStone")
let tileStone26 = SKSpriteNode(imageNamed: "TileStone")
let tileStone27 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop1 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop2 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop3 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop4 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop5 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop6 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop7 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop8 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop9 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop10 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop11 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop12 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop13 = SKSpriteNode(imageNamed: "TileStone")
let player = SKSpriteNode(imageNamed: "PlayerBox")
let enemyBox = SKSpriteNode(imageNamed: "TileStone")
let playerInt : UInt32 = 0
let enemyInt : UInt32 = 1
override func didMoveToView(view: SKView) {
let invisibleNode = SKShapeNode(rect: CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height))
invisibleNode.name = "box"
invisibleNode.fillColor = SKColor.redColor()
invisibleNode.strokeColor = SKColor.clearColor()
self.addChild(invisibleNode)
self.backgroundColor = UIColor(hue: 0.0194, saturation: 0.66, brightness: 0.89, alpha: 1.0)
player.size = CGSize(width: 50, height: 50)
player.position = CGPointMake(self.frame.size.width / 2 - 350, 190)
player.anchorPoint = CGPointZero
player.zPosition = 2
addChild(player)
enemyBox.size = CGSize(width: 50, height: 50)
enemyBox.position = CGPointMake(self.frame.size.width / 2, 215)
enemyBox.zPosition = 2
addChild(enemyBox)
tileStone1.size = CGSize(width: 100, height: 100)
tileStone1.anchorPoint = CGPointZero
tileStone1.position = CGPoint(x: 0, y: 90)
tileStone1.zPosition = 2
addChild(tileStone1)
tileStone2.size = CGSize(width: 100, height: 100)
tileStone2.zPosition = 2
tileStone2.anchorPoint = CGPointZero
tileStone2.position = CGPoint(x: 100, y: 90)
addChild(tileStone2)
tileStone3.size = CGSize(width: 100, height: 100)
tileStone3.zPosition = 2
tileStone3.anchorPoint = CGPointZero
tileStone3.position = CGPoint(x: 200, y: 90)
addChild(tileStone3)
tileStone4.size = CGSize(width: 100, height: 100)
tileStone4.zPosition = 2
tileStone4.anchorPoint = CGPointZero
tileStone4.position = CGPoint(x: 300, y: 90)
addChild(tileStone4)
tileStone5.size = CGSize(width: 100, height: 100)
tileStone5.anchorPoint = CGPointZero
tileStone5.position = CGPoint(x: 400, y: 90)
tileStone5.zPosition = 2
addChild(tileStone5)
tileStone6.size = CGSize(width: 100, height: 100)
tileStone6.anchorPoint = CGPointZero
tileStone6.position = CGPoint(x: 500, y: 90)
tileStone6.zPosition = 2
addChild(tileStone6)
tileStone7.size = CGSize(width: 100, height: 100)
tileStone7.anchorPoint = CGPointZero
tileStone7.position = CGPoint(x: 600, y: 90)
tileStone7.zPosition = 2
addChild(tileStone7)
tileStone8.size = CGSize(width: 100, height: 100)
tileStone8.anchorPoint = CGPointZero
tileStone8.position = CGPoint(x: 700, y: 90)
tileStone8.zPosition = 2
addChild(tileStone8)
tileStone9.size = CGSize(width: 100, height: 100)
tileStone9.anchorPoint = CGPointZero
tileStone9.position = CGPoint(x: 800, y: 90)
tileStone9.zPosition = 2
addChild(tileStone9)
tileStone10.size = CGSize(width: 100, height: 100)
tileStone10.anchorPoint = CGPointZero
tileStone10.position = CGPoint(x: 900, y: 90)
tileStone10.zPosition = 2
addChild(tileStone10)
addChild(tileStone11)
tileStone11.size = CGSize(width: 100, height: 100)
tileStone11.anchorPoint = CGPointZero
tileStone11.position = CGPoint(x: 1000, y: 90)
tileStone11.zPosition = 2
tileStone12.size = CGSize(width: 100, height: 100)
tileStone12.anchorPoint = CGPointZero
tileStone12.position = CGPoint(x: 1100, y: 90)
tileStone12.zPosition = 2
addChild(tileStone12)
tileStone13.size = CGSize(width: 100, height: 100)
tileStone13.anchorPoint = CGPointZero
tileStone13.position = CGPoint(x: 1200, y: 90)
tileStone13.zPosition = 2
addChild(tileStone13)
tileStone14.size = CGSize(width: 100, height: 100)
tileStone14.anchorPoint = CGPointZero
tileStone14.position = CGPoint(x: 1300, y: 90)
tileStone14.zPosition = 2
addChild(tileStone14)
tileStone15.size = CGSize(width: 100, height: 100)
tileStone15.anchorPoint = CGPointZero
tileStone15.position = CGPoint(x: 1400, y: 90)
tileStone15.zPosition = 2
addChild(tileStone15)
tileStone16.size = CGSize(width: 100, height: 100)
tileStone16.anchorPoint = CGPointZero
tileStone16.position = CGPoint(x: 1500, y: 90)
tileStone16.zPosition = 2
addChild(tileStone16)
tileStone17.size = CGSize(width: 100, height: 100)
tileStone17.anchorPoint = CGPointZero
tileStone17.position = CGPoint(x: 1600, y: 90)
tileStone17.zPosition = 2
addChild(tileStone17)
tileStone18.size = CGSize(width: 100, height: 100)
tileStone18.anchorPoint = CGPointZero
tileStone18.position = CGPoint(x: 1700, y: 90)
tileStone18.zPosition = 2
addChild(tileStone18)
tileStone19.size = CGSize(width: 100, height: 100)
tileStone19.anchorPoint = CGPointZero
tileStone19.position = CGPoint(x: 1800, y: 90)
tileStone19.zPosition = 2
addChild(tileStone19)
tileStone20.size = CGSize(width: 100, height: 100)
tileStone20.anchorPoint = CGPointZero
tileStone20.position = CGPoint(x: 1900, y: 90)
tileStone20.zPosition = 2
addChild(tileStone20)
tileStone21.size = CGSize(width: 100, height: 100)
tileStone21.anchorPoint = CGPointZero
tileStone21.position = CGPoint(x: 2000, y: 90)
tileStone21.zPosition = 2
addChild(tileStone21)
tileStone22.size = CGSize(width: 100, height: 100)
tileStone22.anchorPoint = CGPointZero
tileStone22.position = CGPoint(x: 2100, y: 90)
tileStone22.zPosition = 2
addChild(tileStone22)
tileStoneTop1.size = CGSize(width: 100, height: 100)
tileStoneTop1.anchorPoint = CGPointZero
tileStoneTop1.position = CGPoint(x: 0, y: 580)
tileStoneTop1.zPosition = 2
addChild(tileStoneTop1)
tileStoneTop2.size = CGSize(width: 100, height: 100)
tileStoneTop2.zPosition = 2
tileStoneTop2.anchorPoint = CGPointZero
tileStoneTop2.position = CGPoint(x: 100, y: 580)
addChild(tileStoneTop2)
tileStoneTop3.size = CGSize(width: 100, height: 100)
tileStoneTop3.zPosition = 2
tileStoneTop3.anchorPoint = CGPointZero
tileStoneTop3.position = CGPoint(x: 200, y: 580)
addChild(tileStoneTop3)
tileStoneTop4.size = CGSize(width: 100, height: 100)
tileStoneTop4.zPosition = 2
tileStoneTop4.anchorPoint = CGPointZero
tileStoneTop4.position = CGPoint(x: 300, y: 580)
addChild(tileStoneTop4)
tileStoneTop5.size = CGSize(width: 100, height: 100)
tileStoneTop5.anchorPoint = CGPointZero
tileStoneTop5.position = CGPoint(x: 400, y: 580)
tileStoneTop5.zPosition = 2
addChild(tileStoneTop5)
tileStoneTop6.size = CGSize(width: 100, height: 100)
tileStoneTop6.anchorPoint = CGPointZero
tileStoneTop6.position = CGPoint(x: 500, y: 580)
tileStoneTop6.zPosition = 2
addChild(tileStoneTop6)
tileStoneTop7.size = CGSize(width: 100, height: 100)
tileStoneTop7.anchorPoint = CGPointZero
tileStoneTop7.position = CGPoint(x: 600, y: 580)
tileStoneTop7.zPosition = 2
addChild(tileStoneTop7)
tileStoneTop8.size = CGSize(width: 100, height: 100)
tileStoneTop8.anchorPoint = CGPointZero
tileStoneTop8.position = CGPoint(x: 700, y: 580)
tileStoneTop8.zPosition = 2
addChild(tileStoneTop8)
tileStoneTop9.size = CGSize(width: 100, height: 100)
tileStoneTop9.anchorPoint = CGPointZero
tileStoneTop9.position = CGPoint(x: 800, y: 580)
tileStoneTop9.zPosition = 2
addChild(tileStoneTop9)
tileStoneTop10.size = CGSize(width: 100, height: 100)
tileStoneTop10.anchorPoint = CGPointZero
tileStoneTop10.position = CGPoint(x: 900, y: 580)
tileStoneTop10.zPosition = 2
addChild(tileStoneTop10)
tileStoneTop11.size = CGSize(width: 150, height: 100)
tileStoneTop11.anchorPoint = CGPointZero
tileStoneTop11.position = CGPoint(x: 1000, y: 580)
tileStoneTop11.zPosition = 2
addChild(tileStoneTop11)
tileStoneTop12.size = CGSize(width: 200, height: 100)
tileStoneTop12.anchorPoint = CGPointZero
tileStoneTop12.position = CGPoint(x: 1100, y: 5780)
tileStoneTop12.zPosition = 2
addChild(tileStoneTop12)
tileStoneTop13.size = CGSize(width: 100, height: 100)
tileStoneTop13.anchorPoint = CGPointZero
tileStoneTop13.position = CGPoint(x: 1100, y: 5780)
tileStoneTop13.zPosition = 2
// addChild(tileStoneTop13)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let touchLocation = touch.locationInNode(self)
let touchedNode = nodeAtPoint(touchLocation)
if touchedNode.name == "box" {
isTouched = true
}
if touchedNode.name == "rerun"{
for node in self.nodesAtPoint(touchLocation) {
if node.name == "rerun" {
isTouched = false
node.removeFromParent()
}
}
}
}
}
let moveFactor:CGFloat = 0.5
override func update(currentTime: CFTimeInterval) {
var tileStoneArray = [tileStone1, tileStone2, tileStone3, tileStone4, tileStone5, tileStone6, tileStone7, tileStone8, tileStone9, tileStone10, tileStone11, tileStone12, tileStone13, tileStone14, tileStone15, tileStone16, tileStone17, tileStone18, tileStone19, tileStone20, tileStone21, tileStone22, tileStoneTop1, tileStoneTop2, tileStoneTop3, tileStoneTop4, tileStoneTop5, tileStoneTop6, tileStoneTop7, tileStoneTop8, tileStoneTop9, tileStoneTop10, tileStoneTop11, tileStoneTop12, tileStoneTop13]
if isTouched == false {
enemyBox.position = CGPoint(x: enemyBox.position.x - 10, y: 215)
}
if enemyBox.position.x < self.frame.minX - 100 {
enemyBox.position = CGPoint(x: self.frame.maxX, y: 215)
}
for tileStone1 in tileStoneArray {
if isTouched == false {
tileStone1.position = CGPoint(x: tileStone1.position.x - 10, y: tileStone1.position.y)
}
if (tileStone1.position.x < self.frame.minX - 100){
tileStone1.anchorPoint = CGPointZero
tileStone1.position = CGPoint(x: self.frame.maxX, y: tileStone1.position.y)
}
}
var score = 0;
func addGreatNode(){
let congratsLabel = SKLabelNode(fontNamed: "")
congratsLabel.name = "rerun"
congratsLabel.text = "Great!"
congratsLabel.fontSize = 65
congratsLabel.position = CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height / 2)
congratsLabel.fontColor = SKColor.yellowColor()
addChild(congratsLabel)
}
if isTouched == true {
var xValuesEnemy = enemyBox.position.x
var xValuesPlayer = player.position.x
if xValuesPlayer < xValuesEnemy + 120 && xValuesPlayer > xValuesEnemy - 120 {
addGreatNode()
//This is where it adds the node infinitely
}
else {
}
}
}
}
This is a image of my node count climbing while in my game.
I know that I could improve my code to be more efficient but I'm very inexperienced, but I'm working on it.
Your addGreatNode() function is being called within update: which is called by the scene up to 60 times per second.
A few more tips:
Keep your TileStones in a collection
When adding TileStones to your Scene, use either a for or while loop. This will compact your code. Within each iteration, adjust the position for each Node.
If you want to add a SKLabelNode on touch, that logic should be within touchesBegan:, and not associated with update:

Resources