I am working with the joint method in spritekit, but once I join two sprites it seems to change the physics bodies. I have two rectangles joined at a corner and when they fold, I would expect the blue rectangle to sit on top of the white triangle, but this is the result. How do I maintain their physics bodies, so they don't overlap?
import SpriteKit
class GameScene: SKScene {
let character = SKSpriteNode(color: SKColor.whiteColor(), size: CGSizeMake(150, 30))
let character2 = SKSpriteNode(color: SKColor.blueColor(), size: CGSizeMake(150, 30))
let character3 = SKSpriteNode(color: SKColor.greenColor(), size: CGSizeMake(50, 300))
let screenSize: CGRect = UIScreen.mainScreen().bounds
override func didMoveToView(view: SKView) {
// let scene = GameScene(size: SKView.frame)
let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
borderBody.friction = 5
self.physicsBody = borderBody
self.physicsWorld.gravity = CGVectorMake(0.0, -1.0)
character.position = CGPointMake(550, 400)
character2.position = CGPointMake(400, 400)
character3.position = CGPointMake(400, 100)
character.physicsBody = SKPhysicsBody(rectangleOfSize: character.size)
character2.physicsBody = SKPhysicsBody(rectangleOfSize: character2.size)
character3.physicsBody = SKPhysicsBody(rectangleOfSize: character3.size)
self.character.physicsBody!.dynamic = true
self.character2.physicsBody!.dynamic = true
self.character3.physicsBody!.dynamic = true
self.addChild(character)
self.addChild(character2)
self.addChild(character3)
var myJoint = SKPhysicsJointPin.jointWithBodyA(character2.physicsBody, bodyB: character.physicsBody, anchor: CGPoint(x: CGRectGetMaxX(self.character2.frame), y: CGRectGetMinY(self.character.frame)))
self.physicsWorld.addJoint(myJoint)
var myJoint2 = SKPhysicsJointPin.jointWithBodyA(character2.physicsBody, bodyB: character.physicsBody, anchor: CGPoint(x: CGRectGetMaxX(self.character2.frame), y: CGRectGetMaxY(self.character.frame)))
//self.physicsWorld.addJoint(myJoint2)
}
Related
I would like to have one piece of code in my app where I can track the textures of two nodes at a time. If the textures of the two nodes don't match I would like them reset back to their original state before the user touched them. On top of that i would like another piece of code for tracking if the particular node's textures were matched under a particular time frame like 90 seconds. Any tips on how I can do something like this for my app? Thanks.
Here is my current code:
import Foundation
import SpriteKit
class EasyScreen: SKScene {
override func didMove(to view: SKView) {
var background = SKSpriteNode(imageNamed: "Easy Screen Background")
let timerText = SKLabelNode(fontNamed: "Arial")
timerText.fontSize = 40
timerText.fontColor = SKColor.white
timerText.position = CGPoint(x: 20, y: 400)
timerText.zPosition = 1
var counter:Int = 90
timerText.run(SKAction.repeatForever(SKAction.sequence([SKAction.run {
counter-=1
timerText.text = " Time: \(counter)"
print("\(counter)")
if counter <= 0{
let newScene = TryAgainScreen(fileNamed: "Try Again Screen")
newScene?.scaleMode = .aspectFill
self.view?.presentScene(newScene)
}
},SKAction.wait(forDuration: 1)])))
background.position = CGPoint(x: 0, y: 0)
background.size.width = self.size.width
background.size.height = self.size.height
background.anchorPoint = CGPoint(x: 0.5,y: 0.5)
let matchCardOne = SKSpriteNode(imageNamed: "Fruit Match Card")
let matchCardTwo = SKSpriteNode(imageNamed: "Fruit Match Card")
let matchCardThree = SKSpriteNode(imageNamed: "Fruit Match Card")
let matchCardFour = SKSpriteNode(imageNamed: "Fruit Match Card")
matchCardOne.name = "FruitMatchCard1"
matchCardTwo.name = "FruitMatchCard2"
matchCardThree.name = "FruitMatchCard3"
matchCardFour.name = "FruitMatchCard4"
matchCardOne.size = CGSize(width: 150, height: 300)
matchCardTwo.size = CGSize(width: 150, height: 300)
matchCardThree.size = CGSize(width: 150, height: 300)
matchCardFour.size = CGSize(width: 150, height: 300)
matchCardOne.zPosition = 1
matchCardTwo.zPosition = 1
matchCardThree.zPosition = 1
matchCardFour.zPosition = 1
matchCardOne.anchorPoint = CGPoint(x: 0.5, y: 0.5)
matchCardTwo.anchorPoint = CGPoint(x: 0.5, y: 0.5)
matchCardThree.anchorPoint = CGPoint(x: 0.5, y: 0.5)
matchCardFour.anchorPoint = CGPoint(x: 0.5, y: 0.5)
matchCardOne.position = CGPoint(x: -125, y: 60)
matchCardTwo.position = CGPoint(x: -125, y: -260)
matchCardThree.position = CGPoint(x: 70, y: 60)
matchCardFour.position = CGPoint(x: 70 , y: -260)
addChild(background)
addChild(matchCardOne)
addChild(matchCardTwo)
addChild(matchCardThree)
addChild(matchCardFour)
addChild(timerText)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view?.isMultipleTouchEnabled = false
let touch = touches.first
let positionInSceneOne = touch!.location(in: self)
let tappedNodes = nodes(at: positionInSceneOne)
for node in tappedNodes{
if let tappedCard = node as? SKSpriteNode {
if tappedCard.name == "FruitMatchCard1" {
tappedCard.texture = SKTexture(imageNamed: "Apple")
}
}
let touchTwo = touches.first
let positionInSceneTwo = touch!.location(in: self)
let tappedNodesTwo = nodes(at: positionInSceneTwo)
for node in tappedNodesTwo{
if let tappedCard = node as? SKSpriteNode {
if tappedCard.name == "FruitMatchCard2" {
tappedCard.texture = SKTexture(imageNamed: "Apple")
}
}
let touchThree = touches.first
let positionInSceneThree = touch!.location(in: self)
let tappedNodesThree = nodes(at: positionInSceneThree)
for node in tappedNodesThree{
if let tappedCard = node as? SKSpriteNode {
if tappedCard.name == "FruitMatchCard3" {
tappedCard.texture = SKTexture(imageNamed: "Grapes")
}
}
let touchFour = touches.first
let positionInSceneFour = touch!.location(in: self)
let tappedNodesFour = nodes(at: positionInSceneFour)
for node in tappedNodesFour{
if let tappedCard = node as? SKSpriteNode {
if tappedCard.name == "FruitMatchCard4" {
tappedCard.texture = SKTexture(imageNamed: "Grapes")
}
}
}
}
}
}
}
}
An app I'm building drops the FPS when sprite nodes are created, I have searched posts far and wide and cannot figure out why, if anyone had any ideas I would appreciate it!
The issues occur on the simulator and device.
the code for the creating of the nodes is below.
Thank you.
#objc func createEnemy(){
let randomDistribution = GKRandomDistribution(lowestValue: -350, highestValue: 350)
let sprite = SKSpriteNode(imageNamed: "Virus")
sprite.position = CGPoint(x: 700, y: randomDistribution.nextInt())
sprite.name = "Virus"
sprite.zPosition = 1
sprite.size = CGSize(width: 70, height: 70)
addChild(sprite)
sprite.physicsBody = SKPhysicsBody(texture: sprite.texture!, size: sprite.size)
sprite.physicsBody?.velocity = CGVector(dx: -500, dy: 0)
sprite.physicsBody?.linearDamping = 0
sprite.physicsBody?.contactTestBitMask = 1
sprite.physicsBody?.categoryBitMask = 0
sprite.physicsBody?.affectedByGravity = false
createBonus()
}
func createBonus(){
let randomDistribution = GKRandomDistribution(lowestValue: -350, highestValue: 350)
let sprite = SKSpriteNode(imageNamed: "Vaccine")
sprite.position = CGPoint(x: 700, y: randomDistribution.nextInt())
sprite.name = "Vaccine"
sprite.size = CGSize(width: 70, height: 70)
sprite.zPosition = 1
addChild(sprite)
sprite.physicsBody = SKPhysicsBody(texture: sprite.texture!, size: sprite.size)
sprite.physicsBody?.velocity = CGVector(dx: -500, dy: 0)
sprite.physicsBody?.linearDamping = 0
sprite.physicsBody?.contactTestBitMask = 1
sprite.physicsBody?.categoryBitMask = 0
sprite.physicsBody?.collisionBitMask = 0
sprite.physicsBody?.affectedByGravity = false
}
Did you try to preload the textures?
let image = SKTexture(imageNamed: "nodeImage")
override func didMove(to view: SKView) {
image.preload{
print("image has been preloaded")
}
}
I am trying to scale an NSView that is 56x56. The animation is applying scale of 0.95 and an alpha of 0.75, autoreverse and repeats infinitely. I have the animation working however the animation is extremely chunky (not smooth).
How can I use CAAnimationGroup and CABasicAnimation to animate these properties smoothly?
You can see the chunky animation in this gif
The animation code looks like the following
private func transformWithScale(_ scale: CGFloat) -> CATransform3D {
let bounds = squareView.bounds
let scale = scale != 0 ? scale : CGFloat.leastNonzeroMagnitude
let xPadding = 0.5*bounds.width
let yPadding = 0.5*bounds.height
let translate = CATransform3DMakeTranslation(xPadding, yPadding, 0.0)
return scale == 1.0 ? translate : CATransform3DScale(translate, scale, scale, 1.0)
}
func startAnimation() {
let layer = squareView.layer!
layer.removeAllAnimations()
layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
let scaleAnimation = CABasicAnimation(keyPath: "transform")
scaleAnimation.fromValue = transformWithScale(1.0)
scaleAnimation.toValue = transformWithScale(0.95)
let alphaAnimation = CABasicAnimation(keyPath: "opacity")
alphaAnimation.fromValue = 1.0
alphaAnimation.toValue = 0.75
let group = CAAnimationGroup()
group.duration = 0.8
group.autoreverses = true
group.timingFunction = CAMediaTimingFunction(name: .easeIn)
group.repeatCount = .infinity
group.animations = [scaleAnimation, alphaAnimation]
layer.add(group, forKey: "scaleAndAlpha")
}
Try this one:
let container = NSView(frame: NSRect(x: 0, y: 0, width: 300, height: 300))
container.wantsLayer = true
container.layer?.backgroundColor = NSColor.blue.cgColor
let content = NSView(frame: NSRect(x: 0, y: 0, width: 150, height: 150))
content.wantsLayer = true
content.layer?.backgroundColor = NSColor.red.cgColor
container.addSubview(content)
let layer = content.layer!
let scaleAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.transform))
scaleAnimation.fromValue = CATransform3DScale(CATransform3DIdentity, 1, 1, 1)
scaleAnimation.toValue = CATransform3DScale(CATransform3DIdentity, 0.85, 0.85, 1)
let alphaAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
alphaAnimation.fromValue = 1.0
alphaAnimation.toValue = 0.75
let group = CAAnimationGroup()
group.duration = 0.8
group.autoreverses = true
group.timingFunction = CAMediaTimingFunction(name: .easeOut)
group.repeatCount = .infinity
group.animations = [scaleAnimation, alphaAnimation]
let center = CGPoint(x: container.bounds.midX, y: container.bounds.midY)
layer.position = center
layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
layer.add(group, forKey: nil)
PlaygroundPage.current.liveView = container
I have the SKContactDelegate setup
class GameScene: SKScene, SKPhysicsContactDelegate
{
override func didMoveToView(view: SKView)
{
self.physicsWorld.contactDelegate = self
Here I setup the enum
enum ColliderType:UInt32
{
case Player = 1
case Boundary = 2
}
Here I make the player node.
let playerTexture = SKTexture(imageNamed: "testCircle1.png")
thePlayer = SKSpriteNode(texture: playerTexture)
thePlayer.position = CGPointMake(frame.size.width - 350, frame.size.height/2)
thePlayer.zPosition = 5
thePlayer.size = CGSize(width: 100, height: 100)
thePlayer.name = "playerNode"
thePlayer.physicsBody = SKPhysicsBody(circleOfRadius: playerTexture.size().height/2)
thePlayer.physicsBody!.dynamic = false
thePlayer.physicsBody!.allowsRotation = false
thePlayer.physicsBody!.categoryBitMask = ColliderType.Player.rawValue
thePlayer.physicsBody!.contactTestBitMask = ColliderType.Boundary.rawValue
thePlayer.physicsBody!.collisionBitMask = ColliderType.Boundary.rawValue
addChild(thePlayer)
Then I setup the Boundaries
ground = SKSpriteNode(color: UIColor.redColor(), size: CGSizeMake(frame.size.width - 150, 10))
ground.position = CGPointMake(frame.size.width/2 - 75, 96)
ground.zPosition = 5
ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(ground.size.width, ground.size.height))
ground.physicsBody!.dynamic = false
ground.physicsBody!.allowsRotation = false
ground.physicsBody!.categoryBitMask = ColliderType.Boundary.rawValue
ground.physicsBody!.contactTestBitMask = ColliderType.Player.rawValue
ground.physicsBody!.collisionBitMask = ColliderType.Player.rawValue
addChild(ground)
sky = SKSpriteNode(color: UIColor.redColor(), size: CGSizeMake(frame.size.width - 150, 10))
sky.position = CGPointMake(frame.size.width/2 - 75, frame.size.height - 96)
sky.zPosition = 5
sky.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(frame.size.width - 150, 10))
sky.physicsBody!.dynamic = false
sky.physicsBody!.allowsRotation = false
sky.physicsBody!.categoryBitMask = ColliderType.Boundary.rawValue
sky.physicsBody!.contactTestBitMask = ColliderType.Player.rawValue
sky.physicsBody!.collisionBitMask = ColliderType.Player.rawValue
addChild(sky)
Then the collision detection
func didBeginContact(contact: SKPhysicsContact)
{
print("Contact")
if contact.bodyA.categoryBitMask == ColliderType.Boundary.rawValue || contact.bodyB.categoryBitMask == ColliderType.Boundary.rawValue
{
print("Contact")
thePlayer.removeAllActions()
}
}
I tried putting the print outside of the if statement to see if it was even detecting collision at all, but it wasn't.
I have looked at many tutorials and followed what they did but it just won't work and I only have a couple more weeks to turn this in.
In the player node
remove this:
thePlayer.physicsBody!.dynamic = false
That is setting the physics body to be a boundary and not a body...
I've just been playing around with some of the features of XCode/Swift and building a simple game. I'm having difficulty adding a physicsBody to an SKSpriteNode.
I've created a 'Bullet' class and a 'Tank' class (which has an array of 10 'Bullets' as one of its properties).
The sprite property is being assigned properly with a texture. However, the physicsBody is coming back as 'nil'.
class Tank {
var position = CGPoint()
var speed = CGPoint(x: 0, y: 0)
var sprite = SKSpriteNode()
var viewSize = CGPoint() // width, height
var bullets: [Bullet] = []
var bulletNumber = 0
let TOTAL_BULLETS = 10
// ***Tank class constructor
init(playerNum: Int, filename: NSString, tankName: NSString) {
var spaceshipTexture = SKTexture(imageNamed: "Spaceship")
self.sprite = SKSpriteNode(texture: spaceshipTexture, size: spaceshipTexture.size())
self.sprite.physicsBody? = SKPhysicsBody(texture: spaceshipTexture, size: spaceshipTexture.size())
println(self.sprite.physicsBody)
}
// ****Bullet class
class Bullet {
var position = CGPoint(x: -50, y: -50)
var speed = CGPoint(x: 0, y: 0)
var sprite = SKSpriteNode()
var viewSize = CGPoint() // width, height
var isBeingFired = false
init () {
//self.sprite = SKSpriteNode(imageNamed: "Bullet")
var spaceshipTexture = SKTexture(imageNamed: "Spaceship")
self.sprite = SKSpriteNode(texture: spaceshipTexture, size: spaceshipTexture.size())
self.sprite.xScale = 0.04
self.sprite.yScale = 0.04
self.sprite.physicsBody? = SKPhysicsBody(texture: spaceshipTexture, size: spaceshipTexture.size())
self.sprite.physicsBody?.affectedByGravity = false
println(self.sprite.physicsBody)
}
// *** GameScene
class GameScene: SKScene {
var tank1 = Tank(playerNum: 1, filename: "Spaceship", tankName: "Tank1")
let myLabel = SKLabelNode(fontNamed:"Chalkduster")
override func didMoveToView(view: SKView) {
self.addChild(tank1.sprite)
for i in 0 ..< tank1.TOTAL_BULLETS {
self.addChild(tank1.bullets[i].sprite)
}
For simplicity sake, I'm using the same image for both the Tank and the Bullet (just different sizes) to create the SKSpriteNode.
I'm not sure if this would help, but I add an instance of the Tank and all 10 of its bullets as a child to the GameScene.
Your problem is, that you don't add a SKTexture to your SKSpriteNode but an image. If you want to access an SKTexture you need to add it to your node like that:
var spaceshipTexture = SKTexture(imageNamed: "Spaceship")
var sprite = SKSpriteNode(texture: spaceshipTexture, size: spaceshipTexture.size())
Then you will be able to access the texture of your sprite to create your SKPhysicsBody