SpriteKit frame incorrect - swift

I'm creating a simple game. I'm trying to set the frame as an physicsBody so the plane, never falls below the screen. The function below stops it from falling all the way off, but the SKSpriteNode falls just out of view before stopping.
func setBoundry(view: SKView) {
// Create ground
var boundry = SKNode()
boundry.physicsBody = SKPhysicsBody(edgeLoopFromRect: view.frame)
boundry.physicsBody?.dynamic = false
boundry.physicsBody?.contactTestBitMask = PhysicsCategory.Plane
boundry.physicsBody?.categoryBitMask = PhysicsCategory.Boundry | PhysicsCategory.Collidable
self.addChild(boundry)
}
Plane is being created like this...
func createPlane(sceneView: SKView) {
let planeTexture = SKTexture(imageNamed: "planeRed1")
let planeTexture1 = SKTexture(imageNamed: "planeRed2")
let planeTexture2 = SKTexture(imageNamed: "planeRed3")
// Animate plans propeller
let animation = SKAction.animateWithTextures([planeTexture, planeTexture1, planeTexture2], timePerFrame: 0.05)
let makePropellerSpin = SKAction.repeatActionForever(animation)
// Set planes position
plane = SKSpriteNode(texture: planeTexture)
plane.position = CGPointMake(size.width/4, size.height/2)
plane.runAction(makePropellerSpin)
plane.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(plane.size.width, plane.size.height))
plane.physicsBody?.dynamic = true
plane.physicsBody?.allowsRotation = false
plane.physicsBody?.categoryBitMask = PhysicsCategory.Plane
plane.physicsBody?.collisionBitMask = PhysicsCategory.Collidable | PhysicsCategory.Boundry
plane.physicsBody?.contactTestBitMask = PhysicsCategory.Collidable | PhysicsCategory.Boundry
// Set elevation
plane.zPosition = 5
self.addChild(plane)
}

You need to set the contactTestBitMask for your boundry node and also set the physics for your plane-node:
boundry.physicsBody?.contactTestBitMask = PhysicsCategory.YourPlayerEnum
Also check out the collisionBitMask property of the physicsbody.

Related

SpriteKit Inelastic Collision Reducing Velocity

I'm building a pong/breaker game with a ball and non-static blocks. I'd like the ball to never stop moving, but whenever it hits a block it loses velocity.
I have the restitusion = 1 for all sprites involved, I've tried setting the mass equal to each other and the density and the friction = 0. But, the ball still loses velocity on a bounce.
When the ball hits a block I'm removing it in the didBegin(contact:) function. I've also tried delaying the removal and it didn't help.
I'd like for the ball to have a constant velocity, but still be able to interact with the blocks as later I'd like to add blocks that can be hit without immediately being broken. So, the blocks can't be static but the ball needs to have a constant velocity.
My code for creating the ball node:
func ballNode(_ position: CGPoint?) -> SKSpriteNode {
let node = SKSpriteNode()
node.position = position == nil ? CGPoint(x: size.width/2, y: 100) : position!
node.size = CGSize(width: 17, height: 17)
//background
let background = SKShapeNode(circleOfRadius: 8.5)
background.fillColor = UIColor.white
node.addChild(background)
//physics
node.physicsBody = SKPhysicsBody(circleOfRadius: 8.5)
node.physicsBody?.allowsRotation = true
node.physicsBody?.friction = 0
node.physicsBody?.restitution = 1
node.physicsBody?.linearDamping = 0
node.physicsBody?.angularDamping = 0
node.physicsBody?.categoryBitMask = BallCategory
node.physicsBody?.contactTestBitMask = AddBlockBorderCategory | PaddleCategory
node.physicsBody?.collisionBitMask = PaddleCategory | BlockCategory | BorderCategory
return node
}
My code for creating the block node:
func createBlockNode() -> BlockNode {
let width = (size.width-CGFloat(6*layout[0].count))/CGFloat(layout[0].count)
let height = width*0.5
let nodeSize = CGSize(width: width, height: height)
let node = BlockNode(texture: nil, size: nodeSize)
let background = SKShapeNode(rectOf: nodeSize)
background.fillColor = .darkGray
background.strokeColor = .lightGray
//physics
node.physicsBody = SKPhysicsBody(rectangleOf: nodeSize)
node.physicsBody?.restitution = 1
node.physicsBody?.allowsRotation = true
node.physicsBody?.friction = 0
node.physicsBody?.categoryBitMask = BlockCategory
node.physicsBody?.contactTestBitMask = BallCategory
node.addChild(background)
return node
}
And a screen recording:
screen recording of the ball losing velocity
I'm starting the ball using this:
ball!.physicsBody?.applyForce(CGVector(dx: 0, dy: 50))

Gameover on any collision - Swift Spritekit

In my game I have planes moving around. In my game, I want so that if any plane collides with another, it prints "game over". I have added SKPhysicsContactDelegate to my gamescene. I added a physics bodies to my planes. Then, I added this function to my didMoveToView function:
func didBegin(_ contact: SKPhysicsContact){
print("Game over")
}
Now, when I run my game, and the planes collide, nothing prints to the console. How can I change my code so if any plane collides with another (there are more than 2) it prints game over to the console?
Edit: I have set the physics world contact delegate to self. I have not called this function - do I have to - I thought that this function runs when there is a collision in the scene.
Here is my code:
//
// GameScene.swift
// PlaneGame
//
// Created by Lucas Farleigh on 09/04/2017.
// Copyright © 2017 Farleigh Tech. All rights reserved.
//
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
private let Background = SKSpriteNode(imageNamed: "Background")
private let PlaneRed = SKSpriteNode(imageNamed: "PlaneRed")
private let PlaneBlue = SKSpriteNode(imageNamed: "PlaneBlue")
private let PlaneRed2 = SKSpriteNode(imageNamed: "PlaneRed2")
private let PlaneBlue2 = SKSpriteNode(imageNamed: "PlaneBlue2")
private let StartButton = SKLabelNode(fontNamed: "Chalkduster")
private let Trail = SKSpriteNode(imageNamed: "BlueTrail")
private let Center = SKNode()
private let Center2 = SKNode()
var GameHasStarted = false
override func didMove(to view: SKView) {
//Defining the position,size and ZPosition for the background
Background.position = CGPoint(x:self.frame.midX / 2,y: self.frame.midY)
Background.xScale = 10.0
Background.yScale = 10.0
Background.zPosition = -10
addChild(Background)
//Defining a start button - will be used later as a button
StartButton.position = CGPoint(x:self.frame.midX,y:self.frame.minY + 100)
StartButton.text = "Tap Anywhere To Start"
StartButton.fontSize = 50.0
StartButton.name = "StartButton"
addChild(StartButton)
//Setting the planered position and size up for the plane, ready to start the game
PlaneRed.position = CGPoint(x:self.frame.midX - 250,y: self.frame.midY)
PlaneRed.xScale = 0.13
PlaneRed.yScale = 0.13
PlaneRed.zPosition = 10
//Setting the planeblue position and size up for the plane, ready to start the game
PlaneBlue.position = CGPoint(x:self.frame.midX + 250,y: self.frame.midY)
PlaneBlue.xScale = 0.13
PlaneBlue.yScale = -0.13
PlaneBlue.zPosition = 10
//Setting the planered position and size up for the plane, ready to start the game
PlaneRed2.position = CGPoint(x:self.frame.midX,y: self.frame.midY + 250)
PlaneRed2.xScale = -0.13
PlaneRed2.yScale = 0.13
PlaneRed2.zPosition = 10
//Setting the planeblue position and size up for the plane, ready to start the game
PlaneBlue2.position = CGPoint(x:self.frame.midX,y: self.frame.midY - 250)
PlaneBlue2.xScale = 0.13
PlaneBlue2.yScale = 0.13
PlaneBlue2.zPosition = 10
//Making the trail
Trail.position = CGPoint(x:self.frame.midX,y:self.frame.midY)
Trail.xScale = 1.08
Trail.yScale = 1.08
addChild(Trail)
//In order to rotate the planes around a point, we must create the point as an SKNode, and make the planes a child of the point
//The point then rotates bringing the planes with it
//Setting up the point where the plane will rotate around
Center.position = CGPoint(x:self.frame.midX,y:self.frame.midY)
addChild(Center)
Center2.position = CGPoint(x:self.frame.midX,y:self.frame.midY)
addChild(Center2)
Center.addChild(PlaneRed)
Center.addChild(PlaneBlue)
Center2.addChild(PlaneRed2)
Center2.addChild(PlaneBlue2)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//ADDING PHYSICS TO PLANES
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Defining the red planes physics body
PlaneRed.physicsBody = SKPhysicsBody(rectangleOf: PlaneRed.size)
PlaneRed2.physicsBody = SKPhysicsBody(rectangleOf: PlaneRed2.size)
physicsWorld.contactDelegate = self
physicsWorld.gravity = CGVector.zero
func didBegin(contact: SKPhysicsContact){
print("Game over")
}
}
func Start(){
//Defining an SKAction for the plane to orbit the center
let OrbitCenter = SKAction.rotate(byAngle: CGFloat(-2), duration: 3.8)
let Orbit = SKAction.repeatForever(OrbitCenter)
//Creating the action for the center 2 to rotate anti clockwise
let AntiOrbitCenter = SKAction.rotate(byAngle: CGFloat(2), duration: 3.8)
let AntiOrbit = SKAction.repeatForever(AntiOrbitCenter)
//Running the SKAction on the plane
Center.run(Orbit)
Center2.run(AntiOrbit)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?){
for touch in touches{
//Setting up the touch settings - these two variables store the nodes that the user has touched by first defining the location and then checking for nodes at this location
let location = touch.location(in: self)
let node = self.atPoint(location)
if GameHasStarted == false{
Start()
GameHasStarted = true
}
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
First
Your didBegin func needs to be outside of didMove func
override func didMove(to view: SKView) {
}
func didBegin(_ contact: SKPhysicsContact) {
print("Game over")
}
second
you need to setup some physics categories for your objects so they
know what they can collide with, what they can pass through and what
collisions don't matter. You can put this outside your class
declaration
//Game Physics
struct PhysicsCategory {
static let plane: UInt32 = 0x1 << 0
static let plane2: UInt32 = 0x1 << 1
static let obstacle: UInt32 = 0x1 << 2
}
third
You need to add those physics decorations to your objects
//Defining the red planes physics body
PlaneRed.physicsBody = SKPhysicsBody(rectangleOf: PlaneRed.size)
PlaneRed.physicsBody?.categoryBitMask = PhysicsCategory.plane
PlaneRed.physicsBody?.collisionBitMask = PhysicsCategory.plane
PlaneRed.physicsBody?.contactTestBitMask = PhysicsCategory.plane
PlaneRed.physicsBody?.isDynamic = true
PlaneRed2.physicsBody = SKPhysicsBody(rectangleOf: PlaneRed2.size)
PlaneRed2.physicsBody?.categoryBitMask = PhysicsCategory.plane
PlaneRed2.physicsBody?.collisionBitMask = PhysicsCategory.plane
PlaneRed2.physicsBody?.contactTestBitMask = PhysicsCategory.plane
PlaneRed2.physicsBody?.isDynamic = true
PlaneBlue.physicsBody = SKPhysicsBody(rectangleOf: PlaneBlue.size)
PlaneBlue.physicsBody?.categoryBitMask = PhysicsCategory.plane
PlaneBlue.physicsBody?.collisionBitMask = PhysicsCategory.plane
PlaneBlue.physicsBody?.contactTestBitMask = PhysicsCategory.plane
PlaneBlue.physicsBody?.isDynamic = true
PlaneBlue2.physicsBody = SKPhysicsBody(rectangleOf: PlaneBlue2.size)
PlaneBlue2.physicsBody?.categoryBitMask = PhysicsCategory.plane
PlaneBlue2.physicsBody?.collisionBitMask = PhysicsCategory.plane
PlaneBlue2.physicsBody?.contactTestBitMask = PhysicsCategory.plane
PlaneBlue2.physicsBody?.isDynamic = true
self.physicsWorld.contactDelegate = self
self.physicsWorld.gravity = CGVector.zero

Fading action of SKNode() with SKCropNode() children

I have a SKNode() which has SKCropNode() children. I am able to run all kind of actions to rotate and scale my SKNode(), but when I want to use any kind of fading actions the result always is alpha 1 or alpha 0. No way of fading.
By replacing the SKCropNode() with a SKShapeNode() the fading action is working fine.
Can anyone tell my why? Isn't it possible to fade masked nodes?
import SpriteKit
class GameScene: SKScene {
let player = SKNode()
let playerSize = CGFloat(50)
let playerCrop = SKCropNode()
let playerMask = SKSpriteNode(color: SKColor.blackColor(), size: CGSizeMake(CGFloat(100), CGFloat(100)))
let playerCircle = SKShapeNode(circleOfRadius: CGFloat(100))
let playerCenterMask = SKShapeNode(circleOfRadius: CGFloat(100))
let playerCenterCrop = SKCropNode()
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.blackColor()
player.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
player.alpha = 0
addChild(player)
playerCenterMask.lineWidth = 20
playerCenterCrop.maskNode = playerCenterMask
playerMask.position.x = -playerSize
playerMask.position.y = playerSize
playerCircle.lineWidth = 0
playerCircle.fillColor = SKColor.redColor()
playerCrop.addChild(playerCircle)
playerCrop.maskNode = playerMask
playerCenterCrop.addChild(playerCrop)
player.addChild(playerCenterCrop)
// Animations
let playerScale = SKAction.scaleTo(2.0, duration: 5)
let playerFadeIn = SKAction.fadeInWithDuration(5)
let playerAnimation = SKAction.group([playerScale,playerFadeIn])
player.runAction(playerAnimation, completion: {})
}
}
As mentioned in the comments:
SKCropNode uses alpha < 0.5 to not draw. >= 0.5 to draw, and you can't set the blend mode, so it is probably doing source blend mode. Which means it is overwriting the alpha. Children afterwards get blended.
The player context is created, it draws at the given alpha, then the SKCropNode is drawn, overwriting alpha instead of blending.
Run the fade in action on your child, not your parent to get the results you are looking for.
Here is what your source looks like with changes:
import SpriteKit
class GameScene: SKScene {
let player = SKNode()
let playerSize = CGFloat(50)
let playerCrop = SKCropNode()
let playerMask = SKSpriteNode(color: SKColor.blackColor(), size: CGSizeMake(CGFloat(100), CGFloat(100)))
let playerCircle = SKShapeNode(circleOfRadius: CGFloat(100))
let playerCenterMask = SKShapeNode(circleOfRadius: CGFloat(100))
let playerCenterCrop = SKCropNode()
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.blackColor()
player.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
player.alpha = 1
addChild(player)
playerCircle.alpha = 0
playerCenterMask.lineWidth = 20
playerCenterCrop.maskNode = playerCenterMask
playerMask.position.x = -playerSize
playerMask.position.y = playerSize
playerCircle.lineWidth = 0
playerCircle.fillColor = SKColor.redColor()
playerCrop.addChild(playerCircle)
playerCrop.maskNode = playerMask
playerCenterCrop.addChild(playerCrop)
player.addChild(playerCenterCrop)
// Animations
let playerScale = SKAction.scaleTo(2.0, duration: 5)
let playerFadeIn = SKAction.fadeInWithDuration(5)
let playerAnimation = playerScale
player.runAction(playerAnimation, completion: {})
playerCircle.runAction(playerFadeIn, completion: {})
}
}

Make a node that doesn't move after collision iOS 8

I have nodes (sand) falling downwards.
I have other nodes (walls) that are static.
How do I make the walls not move when the sand collides with it?
let wall = SKSpriteNode(texture: chosen)
wall.position = location
wall.physicsBody = SKPhysicsBody(circleOfRadius: sprite.frame.width)
wall.physicsBody?.affectedByGravity = false
wall.physicsBody!.categoryBitMask = BLOCK
self.addChild(wall)
//////////
let sand = SKSpriteNode (imageNamed: img)
var randLoc = arc4random_uniform(26)
sand.position = CGPointMake(location.x - CGFloat(10) + CGFloat(randLoc), location.y)
self.addChild(sand)
//gravity
sand.physicsBody = SKPhysicsBody(circleOfRadius: sand.frame.width)
sand.physicsBody?.affectedByGravity = true
//contact
sand.physicsBody!.categoryBitMask = self.PARTICLE
sand.physicsBody?.collisionBitMask = self.BLOCK | self.PARTICLE
if you have wall.physicsBody?.dynamic = false & sand.physicsBody?.dynamic = true then you should be able to detect the contact without the walls being pushed/moved by the sand. - Daniel Mihaila
https://stackoverflow.com/users/4963031/daniel-mihaila

How to Stop Hero (Character) Movement in SpriteKit?

The main function to walk the Hero this function inside Player.Swift I want to stop walking with Touching position.
This function would be running continues inside GameScene.swift
I want to stop my Hero when user tap to the target Position but after stop Hero whenever user tap on new target location hero moves with new position and stop again.
Here is my code:
{
override init() {
let atlas = SKTextureAtlas(named: "characters")
let texture = atlas.textureNamed("player_ft1")
texture.filteringMode = .Nearest
sprite = AnimatedSprite(texture: texture)
// sprite = SKSpriteNode(texture: texture)
super.init()
addChild(sprite)
name = "player"
// 1
var minDiam = min(sprite.size.width, sprite.size.height)
minDiam = max(minDiam-16.0, 4.0)
let physicsBody = SKPhysicsBody(circleOfRadius: minDiam/2.0)
// 2
physicsBody.usesPreciseCollisionDetection = true
// 3
physicsBody.allowsRotation = false
physicsBody.restitution = 1
physicsBody.friction = 0
physicsBody.linearDamping = 0
physicsBody.collisionBitMask = PhysicsCategory.Boundary
physicsBody.collisionBitMask = PhysicsCategory.Boundary | PhysicsCategory.Wall | PhysicsCategory.Water
//physicsBody.dynamic=false
// 4
self.physicsBody = physicsBody
sprite.facingBackAnim = AnimatedSprite.createAnimWithPrifix("player",suffix: "ft")
sprite.facingForwardAnim = AnimatedSprite.createAnimWithPrifix("player",suffix: "bk")
sprite.facingSideAnim = AnimatedSprite.createAnimWithPrifix("player",suffix: "lt")
sprite.runAction(sprite.facingBackAnim)
}
func moveToward(target: CGPoint) {
// _Lastlocation
let targetVector = (target - position).normalized() * 150.0
physicsBody?.velocity = CGVector(point: targetVector)
faceCurrentDirection()
}